From b43a9ce6a7e8cd7c10f600ff3034973eb3ba9a14 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 19 Sep 2020 15:04:44 -0400 Subject: [PATCH 01/56] first draft of proposal for basic syntax --- proposals/basic-syntax.md | 194 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 proposals/basic-syntax.md diff --git a/proposals/basic-syntax.md b/proposals/basic-syntax.md new file mode 100644 index 0000000000000..5173d8a31fba7 --- /dev/null +++ b/proposals/basic-syntax.md @@ -0,0 +1,194 @@ +# Syntax of Basic Carbon Features + +The purpose of this proposal is to establish some basic syntactic +elements of the Carbon language and make sure that the grammar is +unambiguous and can be parsed by an LALR parser such as `yacc` or +`bison`. The grammar presented here has indeed been checked by +`bison`. The language features in this basic grammar include control +flow via `if` and `while`, functions, simple structures, choice, and +pattern matching. The main syntactic categories are `declaration`, +`statement`, and `expression`. Establishing these syntactic categories +should help the other proposals choose syntax that is compatible with +the rest of the language. + +The grammar is based on the Carbon language overview and on the +proposals for pattern matching, structs, tuples, and +metaprogramming. There may be places that this grammar does not +accurately capture what was intended in those proposal, which should +trigger some useful discussion and revisions. + +We begin with a summary of the three main syntactic categories: + +* `declaration` includes function, structure, and choice definitions. + +* `statement` includes variable definitions, assignment, blocks, `if`, + `while`, `match`, `break`, `continue`, and `return`. + +* `expression` plays several roles. In an initial attempt these roles + were separate, with three different syntactic categories, but that + led to ambiguities in the grammar. Folding them into one category + resolved the ambiguities. The intent is that the three roles + are teased apart during type checking. + + 1. The `expression` category plays the usual role of expressions + that produce a value, such as integer literals and arithmetic + expression. + + 2. To fascilitate metaprogramming and reflection, `expression` + also includes type expressions, include literals such as `Int` + and `Bool` and constructors for function types, etc. + + 3. `expression` is also used for patterns, for example, in the + `case` of a `match`, also for describing the parameters of + a function, and on the left-hand side of variable definitions. + + +## Expressions + +The following grammar defines the concrete syntax for +expressions. Below we comment on a few unusual aspects of the grammar. + + expression: + identifier + | expression '.' identifier + | expression '[' expression ']' + | expression ':' identifier + | integer_literal + | "true" + | "false" + | '(' field_list ')' + | expression "==" expression + | expression '+' expression + | expression '-' expression + | expression "&&" expression + | expression "||" expression + | '!' expression + | '-' expression + | expression '(' field_list ')' + | "Int" + | "Bool" + | "Type" + | "auto" + | "fn" expression "->" expression + ; + field_list: + /* empty */ + | field + | field ',' field_list + ; + field: + expression + | identifier '=' expression + ; + +The grammar rule + + expression: expression ':' identifier + +is for pattern variables. For example, in a variable definition such as + + var Int: x = 0; + +the `Int: x` is parsed with the grammar rule for pattern variables. +In the above grammar rule, the `expression` to the left of the `:` +must evaluate to a type at compile time. + +The grammar rule + + expression: '(' field_list ')' + +is primarily for constructing a tuple, but it is also used for +creating tuple types and tuple patterns, depending on the context in +which the expression occurs. + +## Statements + +The following grammar defines the concrete syntax for statements. + + statement: + "var" expression '=' expression ';' + | expression '=' expression ';' + | expression ';' + | "if" '(' expression ')' statement "else" statement + | "while" '(' expression ')' statement + | "break" ';' + | "continue" ';' + | "return" expression ';' + | '{' statement_list '}' + | "match" '(' expression ')' '{' clause_list '}' + ; + statement_list: + statement + | statement statement_list + ; + clause_list: + /* empty */ + | clause clause_list + ; + clause: + "case" expression "=>" statement + | "default" "=>" statement + ; + +In the grammar rule for the variable definition statement + + statement: "var" expression '=' expression ';' + +the left-hand-side `expression` is used as a pattern, so it would +typically evaluate to a variable pattern or some other kind of value +(such as a tuple) that contains variable patterns. + +Likewise, in the rule for `case` + + clause: "case" expression "=>" statement + +the `expression` is used as a pattern. + + +# Declarations + +The following grammar defines the syntax for declarations. + + declaration: + "fn" identifier expression "->" expression '{' statement_list '}' + | "struct" identifier '{' member_list '}' + | "choice" identifier '{' alternative_list '}' + ; + member: + "var" expression ':' identifier ';' + | "method" identifier expression "->" expression '{' statement_list '}' + ; + member_list: + /* empty */ + | member member_list + ; + alternative: + "alt" identifier expression ';' + ; + alternative_list: + /* empty */ + | alternative alternative_list + ; + declaration_list: + /* empty */ + | declaration declaration_list + ; + +In the grammar fule for function definitions + + declaration: "fn" identifier expression "->" expression '{' statement_list '}' + +the `expression` before the `->` is used as a pattern to describe the +parameters of the function whereas the `expression` after the `->` +must evaluate to a type at compile-time, to express the return type of +the function. The story is the same for method definitions. + +In the rule for field declarations + + member: "var" expression ':' identifier ';' + +the `expression` must evaluate to a type at compile time. +The same is true for the `expression` in the grammar +rule for an alternative: + + alternative: "alt" identifier expression ';' From 53b8dd518812ae17cf3ade585713d3e562eb3c89 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 19 Sep 2020 15:08:50 -0400 Subject: [PATCH 02/56] rename proposal --- proposals/{basic-syntax.md => p0162.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename proposals/{basic-syntax.md => p0162.md} (100%) diff --git a/proposals/basic-syntax.md b/proposals/p0162.md similarity index 100% rename from proposals/basic-syntax.md rename to proposals/p0162.md From f048c11a10fcaad5608ec9bd628307fd2246ef0a Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 19 Sep 2020 15:16:14 -0400 Subject: [PATCH 03/56] formating --- proposals/p0162.md | 52 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 5173d8a31fba7..12f4935654d25 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -1,5 +1,25 @@ # Syntax of Basic Carbon Features + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/0162) + +## Table of contents + + + +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) + + + +## Problem + The purpose of this proposal is to establish some basic syntactic elements of the Carbon language and make sure that the grammar is unambiguous and can be parsed by an LALR parser such as `yacc` or @@ -11,12 +31,16 @@ pattern matching. The main syntactic categories are `declaration`, should help the other proposals choose syntax that is compatible with the rest of the language. -The grammar is based on the Carbon language overview and on the -proposals for pattern matching, structs, tuples, and +## Background + +The grammar proposed here is based on the Carbon language overview and +on the proposals for pattern matching, structs, tuples, and metaprogramming. There may be places that this grammar does not accurately capture what was intended in those proposal, which should trigger some useful discussion and revisions. +## Proposal + We begin with a summary of the three main syntactic categories: * `declaration` includes function, structure, and choice definitions. @@ -24,11 +48,11 @@ We begin with a summary of the three main syntactic categories: * `statement` includes variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. -* `expression` plays several roles. In an initial attempt these roles +* `expression` plays three roles. In an initial attempt these roles were separate, with three different syntactic categories, but that led to ambiguities in the grammar. Folding them into one category - resolved the ambiguities. The intent is that the three roles - are teased apart during type checking. + resolved the ambiguities. The three roles will be teased apart in + the static and dynamic semantics. 1. The `expression` category plays the usual role of expressions that produce a value, such as integer literals and arithmetic @@ -42,8 +66,7 @@ We begin with a summary of the three main syntactic categories: `case` of a `match`, also for describing the parameters of a function, and on the left-hand side of variable definitions. - -## Expressions +### Expressions The following grammar defines the concrete syntax for expressions. Below we comment on a few unusual aspects of the grammar. @@ -101,7 +124,7 @@ is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in which the expression occurs. -## Statements +### Statements The following grammar defines the concrete syntax for statements. @@ -145,7 +168,7 @@ Likewise, in the rule for `case` the `expression` is used as a pattern. -# Declarations +### Declarations The following grammar defines the syntax for declarations. @@ -192,3 +215,14 @@ The same is true for the `expression` in the grammar rule for an alternative: alternative: "alt" identifier expression ';' + + + + + + + + + + + From 86cedc3d36a95de336cb38332df51e5f2ec72fe9 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 19 Sep 2020 15:58:31 -0400 Subject: [PATCH 04/56] fixing typos --- proposals/p0162.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 12f4935654d25..29f0443ac88dc 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -63,8 +63,8 @@ We begin with a summary of the three main syntactic categories: and `Bool` and constructors for function types, etc. 3. `expression` is also used for patterns, for example, in the - `case` of a `match`, also for describing the parameters of - a function, and on the left-hand side of variable definitions. + `case` of a `match`, in the parameters of a function, and on + the left-hand side of variable definitions. ### Expressions @@ -216,13 +216,3 @@ rule for an alternative: alternative: "alt" identifier expression ';' - - - - - - - - - - From eed621c065449b9f53290f6e06d6b3d7006e28df Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Mon, 21 Sep 2020 13:47:25 -0400 Subject: [PATCH 05/56] precendence and associativity --- proposals/p0162.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/proposals/p0162.md b/proposals/p0162.md index 29f0443ac88dc..a32de47296691 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -216,3 +216,19 @@ rule for an alternative: alternative: "alt" identifier expression ';' +### Precedence and Associativity + +The following defines the precendence and associativity of symbols +used in the grammar. The ordering is from lowest to highest +precendence. + + nonassoc '{' '}' + nonassoc ':' ',' + nonassoc '=' + left "||" + left "&&" + nonassoc "==" + left '+' '-' + nonassoc '!' '*' '&' + left '.' "->" + nonassoc '(' ')' '[' ']' From e26781cfb96131dc25b0ea29e1ea08fe4ce00f36 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Mon, 21 Sep 2020 13:54:41 -0400 Subject: [PATCH 06/56] minor edit --- proposals/p0162.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index a32de47296691..7dbb2444e24b9 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -220,15 +220,16 @@ rule for an alternative: The following defines the precendence and associativity of symbols used in the grammar. The ordering is from lowest to highest -precendence. +precendence. The main goal of the choices here is to stay close to +C++. nonassoc '{' '}' nonassoc ':' ',' - nonassoc '=' + right '=' left "||" left "&&" - nonassoc "==" + left "==" left '+' '-' - nonassoc '!' '*' '&' + right '!' '*' '&' left '.' "->" nonassoc '(' ')' '[' ']' From c8d833e45842d0b75bb0ba6943b0b999c07c40c2 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Mon, 21 Sep 2020 21:15:45 -0400 Subject: [PATCH 07/56] added abstract syntax --- proposals/p0162.md | 119 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 7dbb2444e24b9..3d3355ed90b37 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -41,7 +41,8 @@ trigger some useful discussion and revisions. ## Proposal -We begin with a summary of the three main syntactic categories: +We summarize the three main syntactic categories here and define the +grammar in the next section. * `declaration` includes function, structure, and choice definitions. @@ -66,6 +67,10 @@ We begin with a summary of the three main syntactic categories: `case` of a `match`, in the parameters of a function, and on the left-hand side of variable definitions. +The proposal also specifies the abstract syntax. + +## Details + ### Expressions The following grammar defines the concrete syntax for @@ -170,7 +175,7 @@ the `expression` is used as a pattern. ### Declarations -The following grammar defines the syntax for declarations. +The following grammar defines the concrete syntax for declarations. declaration: "fn" identifier expression "->" expression '{' statement_list '}' @@ -233,3 +238,113 @@ C++. right '!' '*' '&' left '.' "->" nonassoc '(' ')' '[' ']' + +### Abstract Syntax + +The output of parsing is an abstract syntax tree. There are many ways +to define abstract syntax. Here I'll simply use C-style `struct` +definitions. + + +#### Abstract Syntax for Expressions + + enum ExpKind { Variable, PatternVariable, Dereference, Int, Bool, + PrimitiveOp, Call, Tuple, Index, GetField, + IntT, BoolT, TypeT, FunctionT, AutoT }; + enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; + + struct Exp { + ExpKind tag; + union { + string* variable; + struct { Exp* exp; string* field; } get_field; + struct { Exp* exp; Exp* i; } index; + struct { string* name; Exp* type; } pattern_variable; + int integer; + bool boolean; + struct { vector >* args; } tuple; + struct { Operator op; vector* args; } primitive_op; + struct { Exp* fun; Exp* arg; } call; + struct { Exp* param; Exp* ret; } function_type; + } u; + }; + +The correspondence between most of the grammar rules and the abstract +syntax is straightforward. However, the parsing of a function call +deserves a little explanation. Recall the grammar rule: + + expression: expression '(' field_list ')' + +The result of parsing the expression is placed in the `fun` field of +`call`. If `field_list` only contains a single unlabeled entry, then +the result of parsing that entry is placed in `arg`. Otherwise a +`tuple` expression is created from the result of parsing `field_list` +and the tuple is placed in the `arg` field. + +The `field_list` also deserves some explanation. +The fields can be labeled with the grammar rule: + + field: identifier '=' expression + +or unlabeled, with the rule + + field: expression + +The unlabeled fields are given numbers (represented as strings) for +field labels, starting with 0 and going up from left to right. + + +#### Abstract Syntax for Statements + + enum StmtKind { ExpStmt, Assign, VarDef, Delete, If, Return, Seq, Block, + While, Break, Continue, Match }; + + struct Stmt { + int lineno; + StmtKind tag; + union { + Exp* exp; + struct { Exp* lhs; Exp* rhs; } assign; + struct { Exp* pat; Exp* init; } variable_definition; + struct { Exp* cond; Stmt* thn; Stmt* els; } if_stmt; + Exp* return_stmt; + struct { Stmt* stmt; Stmt* next; } sequence; + struct { Stmt* stmt; } block; + struct { Exp* cond; Stmt* body; } while_stmt; + struct { Exp* exp; list< pair >* clauses; } match_stmt; + } u; + }; + +#### Abstract Syntax for Declarations + + struct FunDef { + string name; + Exp* param_pattern; + Exp* return_type; + Stmt* body; + }; + + enum MemberKind { FieldMem }; + + struct Member { + MemberKind tag; + union { + struct { string* name; Exp* type; } field; + } u; + }; + + struct StructDef { + string* name; + list* members; + }; + + enum DeclKind { FunDecl, StructDecl, ChoiceDecl }; + + struct Decl { + DeclKind tag; + union { + struct FunDef* fun_def; + struct StructDef* struct_def; + struct { string* name; list >* alts; } choice_def; + } u; + }; From 974d91aaadc9aeefbc80bdd3c3ca21a114f2aa36 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 23 Sep 2020 14:49:07 -0400 Subject: [PATCH 08/56] some revisions based on feedback from meeting today --- proposals/p0162.md | 47 +++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 3d3355ed90b37..de56b8d2d8219 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -84,7 +84,7 @@ expressions. Below we comment on a few unusual aspects of the grammar. | integer_literal | "true" | "false" - | '(' field_list ')' + | tuple | expression "==" expression | expression '+' expression | expression '-' expression @@ -92,12 +92,12 @@ expressions. Below we comment on a few unusual aspects of the grammar. | expression "||" expression | '!' expression | '-' expression - | expression '(' field_list ')' - | "Int" - | "Bool" - | "Type" + | expression tuple | "auto" - | "fn" expression "->" expression + | "fn" tuple "->" expression + ; + tuple: + '(' field_list ')' ; field_list: /* empty */ @@ -123,7 +123,7 @@ must evaluate to a type at compile time. The grammar rule - expression: '(' field_list ')' + tuple: '(' field_list ')' is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in @@ -178,7 +178,8 @@ the `expression` is used as a pattern. The following grammar defines the concrete syntax for declarations. declaration: - "fn" identifier expression "->" expression '{' statement_list '}' + "fn" identifier tuple "->" expression '{' statement_list '}' + | "fn" identifier tuple "->" expression ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' ; @@ -204,12 +205,14 @@ The following grammar defines the concrete syntax for declarations. In the grammar fule for function definitions - declaration: "fn" identifier expression "->" expression '{' statement_list '}' + declaration: "fn" identifier tuple "->" expression '{' statement_list '}' the `expression` before the `->` is used as a pattern to describe the parameters of the function whereas the `expression` after the `->` must evaluate to a type at compile-time, to express the return type of -the function. The story is the same for method definitions. +the function. The story is the same for method definitions. +The grammar for function definitions does not currently include implicit +parameters, but the intent is to add them to the grammar in the future. In the rule for field declarations @@ -270,19 +273,9 @@ definitions. }; The correspondence between most of the grammar rules and the abstract -syntax is straightforward. However, the parsing of a function call -deserves a little explanation. Recall the grammar rule: - - expression: expression '(' field_list ')' - -The result of parsing the expression is placed in the `fun` field of -`call`. If `field_list` only contains a single unlabeled entry, then -the result of parsing that entry is placed in `arg`. Otherwise a -`tuple` expression is created from the result of parsing `field_list` -and the tuple is placed in the `arg` field. - -The `field_list` also deserves some explanation. -The fields can be labeled with the grammar rule: +syntax is straightforward. However, the parsing the `field_list` +deserves some explanation. The fields can be labeled with the grammar +rule: field: identifier '=' expression @@ -293,6 +286,14 @@ or unlabeled, with the rule The unlabeled fields are given numbers (represented as strings) for field labels, starting with 0 and going up from left to right. +Regarding the rule for tuples: + + tuple: '(' field_list ')' + +if the field list only has a single unlabeled item, then the parse +result for that item is returned. Otherwise a `tuple` AST node is +created containing the parse results for the fields. + #### Abstract Syntax for Statements From 2279899800699efdcb68a2abd805cdeb6ee7b43f Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 23 Sep 2020 15:53:52 -0400 Subject: [PATCH 09/56] optional return type --- proposals/p0162.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index de56b8d2d8219..f2ae1878c2ee5 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -178,11 +178,15 @@ the `expression` is used as a pattern. The following grammar defines the concrete syntax for declarations. declaration: - "fn" identifier tuple "->" expression '{' statement_list '}' - | "fn" identifier tuple "->" expression ';' + "fn" identifier tuple return_type '{' statement_list '}' + | "fn" identifier tuple return_type ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' ; + return_type: + /* empty */ + | ARROW expression + ; member: "var" expression ':' identifier ';' | "method" identifier expression "->" expression '{' statement_list '}' From 42068f7474dd6737c1aadea00b7089508dd29be8 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 23 Sep 2020 15:55:30 -0400 Subject: [PATCH 10/56] oops, not optional for function declarations --- proposals/p0162.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index f2ae1878c2ee5..d931d6e406e87 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -179,7 +179,7 @@ The following grammar defines the concrete syntax for declarations. declaration: "fn" identifier tuple return_type '{' statement_list '}' - | "fn" identifier tuple return_type ';' + | "fn" identifier tuple ARROW expression ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' ; From c6daf89502ef688f30e8ae7f2fe6930a2cb67f70 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 24 Sep 2020 10:05:44 -0400 Subject: [PATCH 11/56] replacing abbreviations with full names --- proposals/p0162.md | 121 ++++++++++++++++++++++++++------------------- 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index d931d6e406e87..e41b13c0bb43a 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -33,11 +33,18 @@ the rest of the language. ## Background -The grammar proposed here is based on the Carbon language overview and -on the proposals for pattern matching, structs, tuples, and -metaprogramming. There may be places that this grammar does not -accurately capture what was intended in those proposal, which should -trigger some useful discussion and revisions. +The grammar proposed here is based on the following proposals: + +* Carbon language overview []() +* proposals for pattern matching + [#87](https://github.com/carbon-language/carbon-lang/pull/87), +* structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), +* tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), and +* metaprogramming [#89](https://github.com/carbon-language/carbon-lang/pull/89). + +There may be places that this grammar does not accurately capture what +was intended in those proposal, which should trigger some useful +discussion and revisions. ## Proposal @@ -60,8 +67,8 @@ grammar in the next section. expression. 2. To fascilitate metaprogramming and reflection, `expression` - also includes type expressions, include literals such as `Int` - and `Bool` and constructors for function types, etc. + also includes type expressions, such as function types + and variables that are aliases for types. 3. `expression` is also used for patterns, for example, in the `case` of a `match`, in the parameters of a function, and on @@ -209,14 +216,13 @@ The following grammar defines the concrete syntax for declarations. In the grammar fule for function definitions - declaration: "fn" identifier tuple "->" expression '{' statement_list '}' + declaration: "fn" identifier tuple return_type '{' statement_list '}' -the `expression` before the `->` is used as a pattern to describe the -parameters of the function whereas the `expression` after the `->` -must evaluate to a type at compile-time, to express the return type of -the function. The story is the same for method definitions. -The grammar for function definitions does not currently include implicit -parameters, but the intent is to add them to the grammar in the future. +the `tuple` is used as a pattern to describe the parameters of the +function whereas the `expression` in the `return_type` must evaluate +to a type at compile-time. The grammar for function definitions does +not currently include implicit parameters, but the intent is to add +them to the grammar in the future. In the rule for field declarations @@ -246,6 +252,9 @@ C++. left '.' "->" nonassoc '(' ')' '[' ']' +For more information on operators and precedence, see proposal +[#168](https://github.com/carbon-language/carbon-lang/pull/168). + ### Abstract Syntax The output of parsing is an abstract syntax tree. There are many ways @@ -255,24 +264,24 @@ definitions. #### Abstract Syntax for Expressions - enum ExpKind { Variable, PatternVariable, Dereference, Int, Bool, - PrimitiveOp, Call, Tuple, Index, GetField, - IntT, BoolT, TypeT, FunctionT, AutoT }; + enum ExpressionKind { Variable, PatternVariable, Dereference, Int, Bool, + PrimitiveOp, Call, Tuple, Index, GetField, + IntT, BoolT, TypeT, FunctionT, AutoT }; enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; - struct Exp { - ExpKind tag; + struct Expression { + ExpressionKind tag; union { string* variable; - struct { Exp* exp; string* field; } get_field; - struct { Exp* exp; Exp* i; } index; - struct { string* name; Exp* type; } pattern_variable; + struct { Expression* exp; string* field; } get_field; + struct { Expression* exp; Expression* i; } index; + struct { string* name; Expression* type; } pattern_variable; int integer; bool boolean; - struct { vector >* args; } tuple; - struct { Operator op; vector* args; } primitive_op; - struct { Exp* fun; Exp* arg; } call; - struct { Exp* param; Exp* ret; } function_type; + struct { vector >* args; } tuple; + struct { Operator op; vector* args; } primitive_op; + struct { Expression* fun; Expression* arg; } call; + struct { Expression* param; Expression* ret; } function_type; } u; }; @@ -301,55 +310,63 @@ created containing the parse results for the fields. #### Abstract Syntax for Statements - enum StmtKind { ExpStmt, Assign, VarDef, Delete, If, Return, Seq, Block, - While, Break, Continue, Match }; + enum StatementKind { ExpressionStatement, Assign, VariableDefinition, + Delete, If, Return, Sequence, Block, While, Break, + Continue, Match }; - struct Stmt { + struct Statement { int lineno; - StmtKind tag; + StatementKind tag; union { - Exp* exp; - struct { Exp* lhs; Exp* rhs; } assign; - struct { Exp* pat; Exp* init; } variable_definition; - struct { Exp* cond; Stmt* thn; Stmt* els; } if_stmt; - Exp* return_stmt; - struct { Stmt* stmt; Stmt* next; } sequence; - struct { Stmt* stmt; } block; - struct { Exp* cond; Stmt* body; } while_stmt; - struct { Exp* exp; list< pair >* clauses; } match_stmt; + Expression* exp; + struct { Expression* lhs; Expression* rhs; } assign; + struct { Expression* pat; Expression* init; } variable_definition; + struct { Expression* cond; Statement* thn; Statement* els; } if_stmt; + Expression* return_stmt; + struct { Statement* stmt; Statement* next; } sequence; + struct { Statement* stmt; } block; + struct { Expression* cond; Statement* body; } while_stmt; + struct { + Expression* exp; + list< pair >* clauses; + } match_stmt; } u; }; #### Abstract Syntax for Declarations - struct FunDef { + struct FunctionDefinition { string name; - Exp* param_pattern; - Exp* return_type; - Stmt* body; + Expression* param_pattern; + Expression* return_type; + Statement* body; }; - enum MemberKind { FieldMem }; + enum MemberKind { FieldMember }; struct Member { MemberKind tag; union { - struct { string* name; Exp* type; } field; + struct { string* name; Expression* type; } field; } u; }; - struct StructDef { + struct StructDefinition { string* name; list* members; }; - enum DeclKind { FunDecl, StructDecl, ChoiceDecl }; + enum DeclarationKind { FunctionDeclaration, StructDeclaration, + ChoiceDeclaration }; - struct Decl { - DeclKind tag; + struct Declaration { + DeclarationKind tag; union { - struct FunDef* fun_def; - struct StructDef* struct_def; - struct { string* name; list >* alts; } choice_def; + struct FunctionDefinition* fun_def; + struct StructDefinition* struct_def; + struct { + string* name; + list >* alts; + } choice_def; } u; }; From 556deab9ef95f69a67146c9d26f8aaea0a177924 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 24 Sep 2020 14:48:23 -0400 Subject: [PATCH 12/56] name changes --- proposals/p0162.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index e41b13c0bb43a..f2cb087524de3 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -272,16 +272,19 @@ definitions. struct Expression { ExpressionKind tag; union { - string* variable; - struct { Expression* exp; string* field; } get_field; - struct { Expression* exp; Expression* i; } index; + struct { string* name; } variable; + struct { Expression* aggregate; string* field; } get_field; + struct { Expression* aggregate; Expression* offset; } index; struct { string* name; Expression* type; } pattern_variable; int integer; bool boolean; - struct { vector >* args; } tuple; - struct { Operator op; vector* args; } primitive_op; - struct { Expression* fun; Expression* arg; } call; - struct { Expression* param; Expression* ret; } function_type; + struct { vector >* fields; } tuple; + struct { + Operator operator_; + vector* arguments; + } primitive_op; + struct { Expression* function; Expression* argument; } call; + struct { Expression* parameter; Expression* return_type;} function_type; } u; }; @@ -315,7 +318,6 @@ created containing the parse results for the fields. Continue, Match }; struct Statement { - int lineno; StatementKind tag; union { Expression* exp; From 0855d051cb0e5c8b4f88cd2dccc46079a012ef1d Mon Sep 17 00:00:00 2001 From: "Jeremy G. Siek" Date: Thu, 24 Sep 2020 16:59:50 -0400 Subject: [PATCH 13/56] Update proposals/p0162.md Co-authored-by: Dmitri Gribenko --- proposals/p0162.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index f2cb087524de3..219dbff9858d1 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -53,7 +53,7 @@ grammar in the next section. * `declaration` includes function, structure, and choice definitions. -* `statement` includes variable definitions, assignment, blocks, `if`, +* `statement` includes local variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. * `expression` plays three roles. In an initial attempt these roles From b02522b09517664bbd5810fb74c363a71ba2b62c Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 24 Sep 2020 17:02:03 -0400 Subject: [PATCH 14/56] removed = from precedence table --- proposals/p0162.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 219dbff9858d1..14c0082119189 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -243,7 +243,6 @@ C++. nonassoc '{' '}' nonassoc ':' ',' - right '=' left "||" left "&&" left "==" From 43c4b9f44af4e10500c6bad7db11a02684be9818 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Fri, 25 Sep 2020 09:02:59 -0400 Subject: [PATCH 15/56] for fun decl, back to optional return type, shorthand for void return --- proposals/p0162.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 14c0082119189..e3abee454e73c 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -186,7 +186,7 @@ The following grammar defines the concrete syntax for declarations. declaration: "fn" identifier tuple return_type '{' statement_list '}' - | "fn" identifier tuple ARROW expression ';' + | "fn" identifier tuple return_type ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' ; From 28bfaf5476449c4b3a166c4e6bfedd7a5ee3d2cc Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 26 Sep 2020 10:50:07 -0400 Subject: [PATCH 16/56] more fiddling with return types --- proposals/p0162.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index e3abee454e73c..d8a19e3b4e48d 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -101,7 +101,7 @@ expressions. Below we comment on a few unusual aspects of the grammar. | '-' expression | expression tuple | "auto" - | "fn" tuple "->" expression + | "fn" tuple return_type ; tuple: '(' field_list ')' @@ -115,6 +115,10 @@ expressions. Below we comment on a few unusual aspects of the grammar. expression | identifier '=' expression ; + return_type: + /* empty */ + | ARROW expression + ; The grammar rule @@ -190,10 +194,6 @@ The following grammar defines the concrete syntax for declarations. | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' ; - return_type: - /* empty */ - | ARROW expression - ; member: "var" expression ':' identifier ';' | "method" identifier expression "->" expression '{' statement_list '}' From 3ef030ad386cfa6cf7cc6e7c996aa8c46f0ba4ab Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 26 Sep 2020 16:22:21 -0400 Subject: [PATCH 17/56] change base case of statement_list to empty --- proposals/p0162.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index d8a19e3b4e48d..798b0a2579ec6 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -157,7 +157,7 @@ The following grammar defines the concrete syntax for statements. | "match" '(' expression ')' '{' clause_list '}' ; statement_list: - statement + /* empty */ | statement statement_list ; clause_list: From dd4cf5294ded4303ad9c11fc3d068101cfb213d4 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 11:32:49 -0400 Subject: [PATCH 18/56] added pattern non-terminal --- proposals/p0162.md | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 798b0a2579ec6..c705cab846c70 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -83,6 +83,10 @@ The proposal also specifies the abstract syntax. The following grammar defines the concrete syntax for expressions. Below we comment on a few unusual aspects of the grammar. + expression: + pattern + { $$ = $1 } + ; expression: identifier | expression '.' identifier @@ -112,8 +116,8 @@ expressions. Below we comment on a few unusual aspects of the grammar. | field ',' field_list ; field: - expression - | identifier '=' expression + pattern + | identifier '=' pattern ; return_type: /* empty */ @@ -145,7 +149,7 @@ which the expression occurs. The following grammar defines the concrete syntax for statements. statement: - "var" expression '=' expression ';' + "var" pattern '=' expression ';' | expression '=' expression ';' | expression ';' | "if" '(' expression ')' statement "else" statement @@ -165,25 +169,10 @@ The following grammar defines the concrete syntax for statements. | clause clause_list ; clause: - "case" expression "=>" statement + "case" pattern "=>" statement | "default" "=>" statement ; -In the grammar rule for the variable definition statement - - statement: "var" expression '=' expression ';' - -the left-hand-side `expression` is used as a pattern, so it would -typically evaluate to a variable pattern or some other kind of value -(such as a tuple) that contains variable patterns. - -Likewise, in the rule for `case` - - clause: "case" expression "=>" statement - -the `expression` is used as a pattern. - - ### Declarations The following grammar defines the concrete syntax for declarations. @@ -292,11 +281,11 @@ syntax is straightforward. However, the parsing the `field_list` deserves some explanation. The fields can be labeled with the grammar rule: - field: identifier '=' expression + field: identifier '=' pattern or unlabeled, with the rule - field: expression + field: pattern The unlabeled fields are given numbers (represented as strings) for field labels, starting with 0 and going up from left to right. From 34b50f27dccbb3a3297d4431d46105121bb99128 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 11:52:05 -0400 Subject: [PATCH 19/56] added expression style function definitions --- proposals/p0162.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proposals/p0162.md b/proposals/p0162.md index c705cab846c70..37674cec0ded2 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -179,6 +179,7 @@ The following grammar defines the concrete syntax for declarations. declaration: "fn" identifier tuple return_type '{' statement_list '}' + | "fn" identifier tuple return_type "=>" expression ';' | "fn" identifier tuple return_type ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' From 5f95ac380d7f5dc9385c77fd9523fed9e439e4da Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 12:00:41 -0400 Subject: [PATCH 20/56] change && to and, || to or --- proposals/p0162.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 37674cec0ded2..6aca11a24009f 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -99,8 +99,8 @@ expressions. Below we comment on a few unusual aspects of the grammar. | expression "==" expression | expression '+' expression | expression '-' expression - | expression "&&" expression - | expression "||" expression + | expression "and" expression + | expression "or" expression | '!' expression | '-' expression | expression tuple @@ -179,7 +179,7 @@ The following grammar defines the concrete syntax for declarations. declaration: "fn" identifier tuple return_type '{' statement_list '}' - | "fn" identifier tuple return_type "=>" expression ';' + | "fn" identifier tuple "=>" expression ';' | "fn" identifier tuple return_type ';' | "struct" identifier '{' member_list '}' | "choice" identifier '{' alternative_list '}' From 2cc4f67234969d9aaf5c9943c528782cf15410ad Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 12:03:35 -0400 Subject: [PATCH 21/56] change and to have equal precedence --- proposals/p0162.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 6aca11a24009f..5a7f622abd905 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -233,8 +233,7 @@ C++. nonassoc '{' '}' nonassoc ':' ',' - left "||" - left "&&" + left "or" "and" left "==" left '+' '-' right '!' '*' '&' From e0afba24f7d9fdc67f85de1d8551f2210498e7c1 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 12:35:29 -0400 Subject: [PATCH 22/56] updating text to match grammar, fix typo in grammar regarding pattern --- proposals/p0162.md | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 5a7f622abd905..c76435de87966 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -48,7 +48,7 @@ discussion and revisions. ## Proposal -We summarize the three main syntactic categories here and define the +We summarize the four main syntactic categories here and define the grammar in the next section. * `declaration` includes function, structure, and choice definitions. @@ -56,23 +56,19 @@ grammar in the next section. * `statement` includes local variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. -* `expression` plays three roles. In an initial attempt these roles - were separate, with three different syntactic categories, but that - led to ambiguities in the grammar. Folding them into one category - resolved the ambiguities. The three roles will be teased apart in - the static and dynamic semantics. +* `expression` plays two roles: for value-producing expressions and + for type-producing expressions. In an initial attempt these roles + were separate but that led to ambiguities in the grammar. Folding + them into one category resolved the ambiguities. The two roles + are teased apart in the type system and semantics. - 1. The `expression` category plays the usual role of expressions - that produce a value, such as integer literals and arithmetic - expression. +* `pattern` for the patterns in a `match` statement, for the left-hand + side of a variable definition, and for describing the parameters + of a function. The grammar treats patterns and expressions + identically, but the type system will only allow pattern variables + (`expression ':' identifier`) in patterns and not in value or + type-producing expressions. - 2. To fascilitate metaprogramming and reflection, `expression` - also includes type expressions, such as function types - and variables that are aliases for types. - - 3. `expression` is also used for patterns, for example, in the - `case` of a `match`, in the parameters of a function, and on - the left-hand side of variable definitions. The proposal also specifies the abstract syntax. @@ -87,7 +83,7 @@ expressions. Below we comment on a few unusual aspects of the grammar. pattern { $$ = $1 } ; - expression: + pattern: identifier | expression '.' identifier | expression '[' expression ']' @@ -126,7 +122,7 @@ expressions. Below we comment on a few unusual aspects of the grammar. The grammar rule - expression: expression ':' identifier + pattern: expression ':' identifier is for pattern variables. For example, in a variable definition such as From fa2695c6def848f6dc02c7536cbf2acae2e7b8ab Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 13:22:09 -0400 Subject: [PATCH 23/56] adding trailing comma thing to tuples --- proposals/p0162.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index c76435de87966..b625bd1cc2bea 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -290,9 +290,10 @@ Regarding the rule for tuples: tuple: '(' field_list ')' -if the field list only has a single unlabeled item, then the parse -result for that item is returned. Otherwise a `tuple` AST node is -created containing the parse results for the fields. +if the field list only has a single unlabeled item without a trailing +comma, then the parse result for that item is returned. Otherwise a +`tuple` AST node is created containing the parse results for the +fields. #### Abstract Syntax for Statements From dc80cbdbfdcb34614ae51cf70d049a989537cec5 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 27 Sep 2020 14:32:41 -0400 Subject: [PATCH 24/56] removed 'alt' keyword, not necessary --- proposals/p0162.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index b625bd1cc2bea..4b70e44b48f6d 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -189,7 +189,7 @@ The following grammar defines the concrete syntax for declarations. | member member_list ; alternative: - "alt" identifier expression ';' + identifier expression ';' ; alternative_list: /* empty */ @@ -218,7 +218,7 @@ the `expression` must evaluate to a type at compile time. The same is true for the `expression` in the grammar rule for an alternative: - alternative: "alt" identifier expression ';' + alternative: identifier expression ';' ### Precedence and Associativity From c611253f916033a8c71bd86c6a71a783b567ab0a Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Mon, 28 Sep 2020 10:07:22 -0400 Subject: [PATCH 25/56] flipped expression and pattern --- proposals/p0162.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 4b70e44b48f6d..4320da640ad19 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -79,11 +79,11 @@ The proposal also specifies the abstract syntax. The following grammar defines the concrete syntax for expressions. Below we comment on a few unusual aspects of the grammar. - expression: - pattern + pattern: + expression { $$ = $1 } ; - pattern: + expression: identifier | expression '.' identifier | expression '[' expression ']' @@ -122,7 +122,7 @@ expressions. Below we comment on a few unusual aspects of the grammar. The grammar rule - pattern: expression ':' identifier + expression: expression ':' identifier is for pattern variables. For example, in a variable definition such as From e780d817651811011aa863cdf07289cb6dad4ae5 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 30 Sep 2020 16:24:46 -0400 Subject: [PATCH 26/56] added period for named arguments and documented the reason --- proposals/p0162.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 4320da640ad19..bb7b3451d3aef 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -113,7 +113,7 @@ expressions. Below we comment on a few unusual aspects of the grammar. ; field: pattern - | identifier '=' pattern + | '.' identifier '=' pattern ; return_type: /* empty */ @@ -140,6 +140,17 @@ is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in which the expression occurs. +Regarding the grammar rule for a named argument + + field: '.' identifier '=' pattern + +the period is needed to aid integrated development environments with +auto-completion. The issue is that without the period, when the +programmer is typing the identifier (and not yet typed the `=`), the +IDE doesn't know whether the identifier is for a positional argument +or whether it is a field name. + + ### Statements The following grammar defines the concrete syntax for statements. @@ -277,7 +288,7 @@ syntax is straightforward. However, the parsing the `field_list` deserves some explanation. The fields can be labeled with the grammar rule: - field: identifier '=' pattern + field: '.' identifier '=' pattern or unlabeled, with the rule From 24c2eac7432be23f392a17445f2de945bc45be73 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 30 Sep 2020 16:35:31 -0400 Subject: [PATCH 27/56] change alternative syntax to use tuple instead of expression --- proposals/p0162.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index bb7b3451d3aef..3c1942df12b0e 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -200,7 +200,7 @@ The following grammar defines the concrete syntax for declarations. | member member_list ; alternative: - identifier expression ';' + identifier tuple ';' ; alternative_list: /* empty */ @@ -226,10 +226,10 @@ In the rule for field declarations member: "var" expression ':' identifier ';' the `expression` must evaluate to a type at compile time. -The same is true for the `expression` in the grammar +The same is true for the `tuple` in the grammar rule for an alternative: - alternative: identifier expression ';' + alternative: identifier tuple ';' ### Precedence and Associativity From 6e8dc8afc57a9d9dab0fc973fd279491f3f228ab Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 30 Sep 2020 16:37:49 -0400 Subject: [PATCH 28/56] comment about abstract syntax --- proposals/p0162.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 3c1942df12b0e..d6e5e5246fade 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -254,7 +254,9 @@ For more information on operators and precedence, see proposal The output of parsing is an abstract syntax tree. There are many ways to define abstract syntax. Here I'll simply use C-style `struct` -definitions. +definitions. The definition of the abstract syntax is important +because it is used in the specification of the semantic analysis and +the specification of runtime behavior. #### Abstract Syntax for Expressions From 097a10db7a2f41b71694cb9134716aed3388e981 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 09:27:09 -0400 Subject: [PATCH 29/56] code block language annotations --- proposals/p0162.md | 369 ++++++++++++++++++++++++--------------------- 1 file changed, 195 insertions(+), 174 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index d6e5e5246fade..de5e205f94251 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -79,50 +79,53 @@ The proposal also specifies the abstract syntax. The following grammar defines the concrete syntax for expressions. Below we comment on a few unusual aspects of the grammar. - pattern: - expression - { $$ = $1 } - ; - expression: - identifier - | expression '.' identifier - | expression '[' expression ']' - | expression ':' identifier - | integer_literal - | "true" - | "false" - | tuple - | expression "==" expression - | expression '+' expression - | expression '-' expression - | expression "and" expression - | expression "or" expression - | '!' expression - | '-' expression - | expression tuple - | "auto" - | "fn" tuple return_type - ; - tuple: - '(' field_list ')' - ; - field_list: - /* empty */ - | field - | field ',' field_list - ; - field: - pattern - | '.' identifier '=' pattern - ; - return_type: - /* empty */ - | ARROW expression - ; +```Bison +pattern: + expression +; +expression: + identifier +| expression '.' identifier +| expression '[' expression ']' +| expression ':' identifier +| integer_literal +| "true" +| "false" +| tuple +| expression "==" expression +| expression '+' expression +| expression '-' expression +| expression "and" expression +| expression "or" expression +| '!' expression +| '-' expression +| expression tuple +| "auto" +| "fn" tuple return_type +; +tuple: + '(' field_list ')' +; +field_list: + /* empty */ +| field +| field ',' field_list +; +field: + pattern +| '.' identifier '=' pattern +; +return_type: + /* empty */ +| ARROW expression +; +``` The grammar rule - expression: expression ':' identifier +```Bison +expression: expression ':' identifier +``` is for pattern variables. For example, in a variable definition such as @@ -134,7 +137,9 @@ must evaluate to a type at compile time. The grammar rule - tuple: '(' field_list ')' +```Bison +tuple: '(' field_list ')' +``` is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in @@ -142,7 +147,9 @@ which the expression occurs. Regarding the grammar rule for a named argument - field: '.' identifier '=' pattern +```Bison +field: '.' identifier '=' pattern +``` the period is needed to aid integrated development environments with auto-completion. The issue is that without the period, when the @@ -155,65 +162,71 @@ or whether it is a field name. The following grammar defines the concrete syntax for statements. - statement: - "var" pattern '=' expression ';' - | expression '=' expression ';' - | expression ';' - | "if" '(' expression ')' statement "else" statement - | "while" '(' expression ')' statement - | "break" ';' - | "continue" ';' - | "return" expression ';' - | '{' statement_list '}' - | "match" '(' expression ')' '{' clause_list '}' - ; - statement_list: - /* empty */ - | statement statement_list - ; - clause_list: - /* empty */ - | clause clause_list - ; - clause: - "case" pattern "=>" statement - | "default" "=>" statement - ; +```Bison +statement: + "var" pattern '=' expression ';' +| expression '=' expression ';' +| expression ';' +| "if" '(' expression ')' statement "else" statement +| "while" '(' expression ')' statement +| "break" ';' +| "continue" ';' +| "return" expression ';' +| '{' statement_list '}' +| "match" '(' expression ')' '{' clause_list '}' +; +statement_list: + /* empty */ +| statement statement_list +; +clause_list: + /* empty */ +| clause clause_list +; +clause: + "case" pattern "=>" statement +| "default" "=>" statement +; +``` ### Declarations The following grammar defines the concrete syntax for declarations. - declaration: - "fn" identifier tuple return_type '{' statement_list '}' - | "fn" identifier tuple "=>" expression ';' - | "fn" identifier tuple return_type ';' - | "struct" identifier '{' member_list '}' - | "choice" identifier '{' alternative_list '}' - ; - member: - "var" expression ':' identifier ';' - | "method" identifier expression "->" expression '{' statement_list '}' - ; - member_list: - /* empty */ - | member member_list - ; - alternative: - identifier tuple ';' - ; - alternative_list: - /* empty */ - | alternative alternative_list - ; - declaration_list: - /* empty */ - | declaration declaration_list - ; +```Bison +declaration: + "fn" identifier tuple return_type '{' statement_list '}' +| "fn" identifier tuple "=>" expression ';' +| "fn" identifier tuple return_type ';' +| "struct" identifier '{' member_list '}' +| "choice" identifier '{' alternative_list '}' +; +member: + "var" expression ':' identifier ';' +| "method" identifier expression "->" expression '{' statement_list '}' +; +member_list: + /* empty */ +| member member_list +; +alternative: + identifier tuple ';' +; +alternative_list: + /* empty */ +| alternative alternative_list +; +declaration_list: + /* empty */ +| declaration declaration_list +; +``` In the grammar fule for function definitions - declaration: "fn" identifier tuple return_type '{' statement_list '}' +```Bison +declaration: "fn" identifier tuple return_type '{' statement_list '}' +``` the `tuple` is used as a pattern to describe the parameters of the function whereas the `expression` in the `return_type` must evaluate @@ -223,7 +236,9 @@ them to the grammar in the future. In the rule for field declarations - member: "var" expression ':' identifier ';' +```Bison +member: "var" expression ':' identifier ';' +``` the `expression` must evaluate to a type at compile time. The same is true for the `tuple` in the grammar @@ -261,29 +276,31 @@ the specification of runtime behavior. #### Abstract Syntax for Expressions - enum ExpressionKind { Variable, PatternVariable, Dereference, Int, Bool, - PrimitiveOp, Call, Tuple, Index, GetField, - IntT, BoolT, TypeT, FunctionT, AutoT }; - enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; - - struct Expression { - ExpressionKind tag; - union { - struct { string* name; } variable; - struct { Expression* aggregate; string* field; } get_field; - struct { Expression* aggregate; Expression* offset; } index; - struct { string* name; Expression* type; } pattern_variable; - int integer; - bool boolean; - struct { vector >* fields; } tuple; - struct { - Operator operator_; - vector* arguments; - } primitive_op; - struct { Expression* function; Expression* argument; } call; - struct { Expression* parameter; Expression* return_type;} function_type; - } u; - }; +```C++ +enum ExpressionKind { Variable, PatternVariable, Dereference, Int, Bool, + PrimitiveOp, Call, Tuple, Index, GetField, + IntT, BoolT, TypeT, FunctionT, AutoT }; +enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; + +struct Expression { + ExpressionKind tag; + union { + struct { string* name; } variable; + struct { Expression* aggregate; string* field; } get_field; + struct { Expression* aggregate; Expression* offset; } index; + struct { string* name; Expression* type; } pattern_variable; + int integer; + bool boolean; + struct { vector >* fields; } tuple; + struct { + Operator operator_; + vector* arguments; + } primitive_op; + struct { Expression* function; Expression* argument; } call; + struct { Expression* parameter; Expression* return_type;} function_type; + } u; +}; +``` The correspondence between most of the grammar rules and the abstract syntax is straightforward. However, the parsing the `field_list` @@ -311,62 +328,66 @@ fields. #### Abstract Syntax for Statements - enum StatementKind { ExpressionStatement, Assign, VariableDefinition, - Delete, If, Return, Sequence, Block, While, Break, - Continue, Match }; - - struct Statement { - StatementKind tag; - union { - Expression* exp; - struct { Expression* lhs; Expression* rhs; } assign; - struct { Expression* pat; Expression* init; } variable_definition; - struct { Expression* cond; Statement* thn; Statement* els; } if_stmt; - Expression* return_stmt; - struct { Statement* stmt; Statement* next; } sequence; - struct { Statement* stmt; } block; - struct { Expression* cond; Statement* body; } while_stmt; - struct { - Expression* exp; - list< pair >* clauses; - } match_stmt; - } u; - }; +```C++ +enum StatementKind { ExpressionStatement, Assign, VariableDefinition, + Delete, If, Return, Sequence, Block, While, Break, + Continue, Match }; + +struct Statement { + StatementKind tag; + union { + Expression* exp; + struct { Expression* lhs; Expression* rhs; } assign; + struct { Expression* pat; Expression* init; } variable_definition; + struct { Expression* cond; Statement* thn; Statement* els; } if_stmt; + Expression* return_stmt; + struct { Statement* stmt; Statement* next; } sequence; + struct { Statement* stmt; } block; + struct { Expression* cond; Statement* body; } while_stmt; + struct { + Expression* exp; + list< pair >* clauses; + } match_stmt; + } u; +}; +``` #### Abstract Syntax for Declarations - struct FunctionDefinition { - string name; - Expression* param_pattern; - Expression* return_type; - Statement* body; - }; - - enum MemberKind { FieldMember }; - - struct Member { - MemberKind tag; - union { - struct { string* name; Expression* type; } field; - } u; - }; - - struct StructDefinition { - string* name; - list* members; - }; - - enum DeclarationKind { FunctionDeclaration, StructDeclaration, - ChoiceDeclaration }; - - struct Declaration { - DeclarationKind tag; - union { - struct FunctionDefinition* fun_def; - struct StructDefinition* struct_def; - struct { - string* name; - list >* alts; - } choice_def; - } u; - }; +```C++ +struct FunctionDefinition { + string name; + Expression* param_pattern; + Expression* return_type; + Statement* body; +}; + +enum MemberKind { FieldMember }; + +struct Member { + MemberKind tag; + union { + struct { string* name; Expression* type; } field; + } u; +}; + +struct StructDefinition { + string* name; + list* members; +}; + +enum DeclarationKind { FunctionDeclaration, StructDeclaration, + ChoiceDeclaration }; + +struct Declaration { + DeclarationKind tag; + union { + struct FunctionDefinition* fun_def; + struct StructDefinition* struct_def; + struct { + string* name; + list >* alts; + } choice_def; + } u; +}; +``` From c1a3e66462638f993ee6d07d6b742cf2e13ba856 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 10:14:50 -0400 Subject: [PATCH 30/56] added alternative designs, some cleanup for pre-commit --- proposals/p0162.md | 98 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 30 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index de5e205f94251..ba97e7a929a67 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -15,6 +15,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) +- [Alternatives considered](#alternatives-considered) @@ -35,11 +36,12 @@ the rest of the language. The grammar proposed here is based on the following proposals: -* Carbon language overview []() +* [Carbon language overview](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design) * proposals for pattern matching - [#87](https://github.com/carbon-language/carbon-lang/pull/87), -* structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), -* tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), and + [#87](https://github.com/carbon-language/carbon-lang/pull/87), +* structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), +* tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), +* sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157), and * metaprogramming [#89](https://github.com/carbon-language/carbon-lang/pull/89). There may be places that this grammar does not accurately capture what @@ -55,7 +57,7 @@ grammar in the next section. * `statement` includes local variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. - + * `expression` plays two roles: for value-producing expressions and for type-producing expressions. In an initial attempt these roles were separate but that led to ambiguities in the grammar. Folding @@ -130,17 +132,17 @@ expression: expression ':' identifier is for pattern variables. For example, in a variable definition such as var Int: x = 0; - + the `Int: x` is parsed with the grammar rule for pattern variables. -In the above grammar rule, the `expression` to the left of the `:` -must evaluate to a type at compile time. +In the right-hand side of the above grammar rule, the `expression` to +the left of the `:` must evaluate to a type at compile time. The grammar rule ```Bison tuple: '(' field_list ')' ``` - + is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in which the expression occurs. @@ -154,8 +156,9 @@ field: '.' identifier '=' pattern the period is needed to aid integrated development environments with auto-completion. The issue is that without the period, when the programmer is typing the identifier (and not yet typed the `=`), the -IDE doesn't know whether the identifier is for a positional argument -or whether it is a field name. +IDE doesn't know whether the identifier is for a positional argument, +in which case it is a variable occurence, or whether the identifier is +a field name. ### Statements @@ -185,7 +188,7 @@ clause_list: ; clause: "case" pattern "=>" statement -| "default" "=>" statement +| "default" "=>" statement ; ``` @@ -222,7 +225,7 @@ declaration_list: ; ``` -In the grammar fule for function definitions +In the grammar rule for function definitions ```Bison declaration: "fn" identifier tuple return_type '{' statement_list '}' @@ -248,9 +251,9 @@ rule for an alternative: ### Precedence and Associativity -The following defines the precendence and associativity of symbols +The following defines the precedence and associativity of symbols used in the grammar. The ordering is from lowest to highest -precendence. The main goal of the choices here is to stay close to +precedence. The main goal of the choices here is to stay close to C++. nonassoc '{' '}' @@ -260,7 +263,7 @@ C++. left '+' '-' right '!' '*' '&' left '.' "->" - nonassoc '(' ')' '[' ']' + nonassoc '(' ')' '[' ']' For more information on operators and precedence, see proposal [#168](https://github.com/carbon-language/carbon-lang/pull/168). @@ -277,8 +280,8 @@ the specification of runtime behavior. #### Abstract Syntax for Expressions ```C++ -enum ExpressionKind { Variable, PatternVariable, Dereference, Int, Bool, - PrimitiveOp, Call, Tuple, Index, GetField, +enum ExpressionKind { Variable, PatternVariable, Dereference, Int, Bool, + PrimitiveOp, Call, Tuple, Index, GetField, IntT, BoolT, TypeT, FunctionT, AutoT }; enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; @@ -292,9 +295,9 @@ struct Expression { int integer; bool boolean; struct { vector >* fields; } tuple; - struct { - Operator operator_; - vector* arguments; + struct { + Operator operator_; + vector* arguments; } primitive_op; struct { Expression* function; Expression* argument; } call; struct { Expression* parameter; Expression* return_type;} function_type; @@ -308,11 +311,11 @@ deserves some explanation. The fields can be labeled with the grammar rule: field: '.' identifier '=' pattern - + or unlabeled, with the rule field: pattern - + The unlabeled fields are given numbers (represented as strings) for field labels, starting with 0 and going up from left to right. @@ -330,7 +333,7 @@ fields. ```C++ enum StatementKind { ExpressionStatement, Assign, VariableDefinition, - Delete, If, Return, Sequence, Block, While, Break, + Delete, If, Return, Sequence, Block, While, Break, Continue, Match }; struct Statement { @@ -339,7 +342,7 @@ struct Statement { Expression* exp; struct { Expression* lhs; Expression* rhs; } assign; struct { Expression* pat; Expression* init; } variable_definition; - struct { Expression* cond; Statement* thn; Statement* els; } if_stmt; + struct { Expression* cond; Statement* then_; Statement* else_; } if_stmt; Expression* return_stmt; struct { Statement* stmt; Statement* next; } sequence; struct { Statement* stmt; } block; @@ -373,10 +376,10 @@ struct Member { struct StructDefinition { string* name; - list* members; + list* members; }; -enum DeclarationKind { FunctionDeclaration, StructDeclaration, +enum DeclarationKind { FunctionDeclaration, StructDeclaration, ChoiceDeclaration }; struct Declaration { @@ -384,10 +387,45 @@ struct Declaration { union { struct FunctionDefinition* fun_def; struct StructDefinition* struct_def; - struct { - string* name; - list >* alts; + struct { + string* name; + list >* alts; } choice_def; } u; }; ``` + +## Alternatives considered + +Expressions, type expressions, and patterns could instead be defined +using completely disjoint grammar productions, but that would require +adding more keywords or symbols to avoid ambiguity and there would be +a fair amount of repetition in grammar productions. + +In this proposal, assignment is a statement. It could instead be an +expression as it is in C and C++. The arguments against +assignment-as-an-expression include 1) it complicates reasoning about +the ordering of side-effects and 2) it can cause confusion between `=` +and `==` [(SEI CERT C Coding +Standard)](https://wiki.sei.cmu.edu/confluence/display/c/EXP45-C.+Do+not+perform+assignments+in+selection+statements) +[Visual Studio +Warning](https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706?view=vs-2019). + +Inside a `choice`, an `alternative` could instead begin with a keyword +such as `alt`, `fn`, or `var`, as discussed in the proposal for +sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157). + +The precedence for `and` and `or` are equal, whereas in C++ `&&` has +higher precedence than `||`. + +The parameters of a function are described using the syntax of a +pattern. There could instead be separate syntax that is special to +function parameters, as there is in C++. + +The parameters of a function type are described using the syntax for +type expression. There could instead be separate syntax that is +special to the parameters of a function type, as there is in C++. + +The `pattern` non-terminal is defined in terms of `expression`. It +could instead be the other way around, defining `expression` in terms +of `pattern`. From c12a1e530764dfbdc0dd76e05c954dd2e94e4c74 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 10:17:06 -0400 Subject: [PATCH 31/56] spell checking --- proposals/p0162.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index ba97e7a929a67..6f3c873612ee0 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -157,7 +157,7 @@ the period is needed to aid integrated development environments with auto-completion. The issue is that without the period, when the programmer is typing the identifier (and not yet typed the `=`), the IDE doesn't know whether the identifier is for a positional argument, -in which case it is a variable occurence, or whether the identifier is +in which case it is a variable occurrence, or whether the identifier is a field name. From 58106f7b39b90bfa1eab0fbd61f7e8c35de15ec0 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 10:26:19 -0400 Subject: [PATCH 32/56] filled out the TOC --- proposals/p0162.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 6f3c873612ee0..79063913d7902 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -15,6 +15,15 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Problem](#problem) - [Background](#background) - [Proposal](#proposal) +- [Details](#details) + - [Expressions and Patterns](#expressions-patterns) + - [Statements](#statements) + - [Declarations](#declarations) + - [Precedence and Associativity](#precedence) + - [Abstract Syntax](#abstract-syntax) + - [Abstract Syntax for Expressions](#abstract-expressions) + - [Abstract Syntax for Statements](#abstract-statements) + - [Abstract Syntax for Declarations](#abstract-declarations) - [Alternatives considered](#alternatives-considered) @@ -76,7 +85,7 @@ The proposal also specifies the abstract syntax. ## Details -### Expressions +### Expressions and Patterns The following grammar defines the concrete syntax for expressions. Below we comment on a few unusual aspects of the grammar. From 8472acf4cf85e2b9d033c98587f785edf55e6b7d Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 10:38:37 -0400 Subject: [PATCH 33/56] minor edits --- proposals/p0162.md | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 79063913d7902..452859f835015 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -1,4 +1,4 @@ -# Syntax of Basic Carbon Features +# Basic Syntax @@ -169,7 +169,6 @@ IDE doesn't know whether the identifier is for a positional argument, in which case it is a variable occurrence, or whether the identifier is a field name. - ### Statements The following grammar defines the concrete syntax for statements. @@ -319,18 +318,24 @@ syntax is straightforward. However, the parsing the `field_list` deserves some explanation. The fields can be labeled with the grammar rule: - field: '.' identifier '=' pattern +```Bison +field: '.' identifier '=' pattern +``` or unlabeled, with the rule - field: pattern +```Bison +field: pattern +``` The unlabeled fields are given numbers (represented as strings) for field labels, starting with 0 and going up from left to right. Regarding the rule for tuples: - tuple: '(' field_list ')' +```Bison +tuple: '(' field_list ')' +``` if the field list only has a single unlabeled item without a trailing comma, then the parse result for that item is returned. Otherwise a From e1149fefa5ef3fa6455d4654ecebb1e1d3a63d99 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 11:18:27 -0400 Subject: [PATCH 34/56] minor edit --- proposals/p0162.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 452859f835015..aaf4018580527 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -53,10 +53,6 @@ The grammar proposed here is based on the following proposals: * sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157), and * metaprogramming [#89](https://github.com/carbon-language/carbon-lang/pull/89). -There may be places that this grammar does not accurately capture what -was intended in those proposal, which should trigger some useful -discussion and revisions. - ## Proposal We summarize the four main syntactic categories here and define the From 5f6ed2422a8e801390ca998bd2c110cea4a3fbe7 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 1 Oct 2020 11:21:09 -0400 Subject: [PATCH 35/56] trying to fix pre-commit error --- proposals/p0162.md | 1 - 1 file changed, 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index aaf4018580527..ec21cc035f18f 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -76,7 +76,6 @@ grammar in the next section. (`expression ':' identifier`) in patterns and not in value or type-producing expressions. - The proposal also specifies the abstract syntax. ## Details From edc65bd4651ccac6f90128c083e448726f6e6124 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Tue, 6 Oct 2020 22:00:27 -0400 Subject: [PATCH 36/56] changes from pre-commit? --- proposals/README.md | 1 + proposals/p0162.md | 211 +++++++++++++++----------------- proposals/semantic-analysis.md~ | 61 +++++++++ 3 files changed, 162 insertions(+), 111 deletions(-) create mode 100644 proposals/semantic-analysis.md~ diff --git a/proposals/README.md b/proposals/README.md index 0e404821525df..887b7af194cdd 100644 --- a/proposals/README.md +++ b/proposals/README.md @@ -35,5 +35,6 @@ request: - [0083 - In-progress design overview](p0083.md) - [0120 - Add idiomatic code performance and developer-facing docs to goals](p0120.md) - [Decision](p0120_decision.md) +- [0162 - Basic Syntax](p0162.md) diff --git a/proposals/p0162.md b/proposals/p0162.md index ec21cc035f18f..18109b8fec339 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -16,65 +16,65 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - [Background](#background) - [Proposal](#proposal) - [Details](#details) - - [Expressions and Patterns](#expressions-patterns) + - [Expressions and Patterns](#expressions-and-patterns) - [Statements](#statements) - [Declarations](#declarations) - - [Precedence and Associativity](#precedence) + - [Precedence and Associativity](#precedence-and-associativity) - [Abstract Syntax](#abstract-syntax) - - [Abstract Syntax for Expressions](#abstract-expressions) - - [Abstract Syntax for Statements](#abstract-statements) - - [Abstract Syntax for Declarations](#abstract-declarations) + - [Abstract Syntax for Expressions](#abstract-syntax-for-expressions) + - [Abstract Syntax for Statements](#abstract-syntax-for-statements) + - [Abstract Syntax for Declarations](#abstract-syntax-for-declarations) - [Alternatives considered](#alternatives-considered) ## Problem -The purpose of this proposal is to establish some basic syntactic -elements of the Carbon language and make sure that the grammar is -unambiguous and can be parsed by an LALR parser such as `yacc` or -`bison`. The grammar presented here has indeed been checked by -`bison`. The language features in this basic grammar include control -flow via `if` and `while`, functions, simple structures, choice, and -pattern matching. The main syntactic categories are `declaration`, -`statement`, and `expression`. Establishing these syntactic categories -should help the other proposals choose syntax that is compatible with -the rest of the language. +The purpose of this proposal is to establish some basic syntactic elements of +the Carbon language and make sure that the grammar is unambiguous and can be +parsed by an LALR parser such as `yacc` or `bison`. The grammar presented here +has indeed been checked by `bison`. The language features in this basic grammar +include control flow via `if` and `while`, functions, simple structures, choice, +and pattern matching. The main syntactic categories are `declaration`, +`statement`, and `expression`. Establishing these syntactic categories should +help the other proposals choose syntax that is compatible with the rest of the +language. ## Background The grammar proposed here is based on the following proposals: -* [Carbon language overview](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design) -* proposals for pattern matching - [#87](https://github.com/carbon-language/carbon-lang/pull/87), -* structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), -* tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), -* sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157), and -* metaprogramming [#89](https://github.com/carbon-language/carbon-lang/pull/89). +- [Carbon language overview](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design) +- proposals for pattern matching + [#87](https://github.com/carbon-language/carbon-lang/pull/87), +- structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), +- tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), +- sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157), + and +- metaprogramming + [#89](https://github.com/carbon-language/carbon-lang/pull/89). ## Proposal -We summarize the four main syntactic categories here and define the -grammar in the next section. +We summarize the four main syntactic categories here and define the grammar in +the next section. -* `declaration` includes function, structure, and choice definitions. +- `declaration` includes function, structure, and choice definitions. -* `statement` includes local variable definitions, assignment, blocks, `if`, +- `statement` includes local variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. -* `expression` plays two roles: for value-producing expressions and - for type-producing expressions. In an initial attempt these roles - were separate but that led to ambiguities in the grammar. Folding - them into one category resolved the ambiguities. The two roles - are teased apart in the type system and semantics. +- `expression` plays two roles: for value-producing expressions and for + type-producing expressions. In an initial attempt these roles were separate + but that led to ambiguities in the grammar. Folding them into one category + resolved the ambiguities. The two roles are teased apart in the type system + and semantics. -* `pattern` for the patterns in a `match` statement, for the left-hand - side of a variable definition, and for describing the parameters - of a function. The grammar treats patterns and expressions - identically, but the type system will only allow pattern variables - (`expression ':' identifier`) in patterns and not in value or - type-producing expressions. +- `pattern` for the patterns in a `match` statement, for the left-hand side of + a variable definition, and for describing the parameters of a function. The + grammar treats patterns and expressions identically, but the type system + will only allow pattern variables (`expression ':' identifier`) in patterns + and not in value or type-producing expressions. The proposal also specifies the abstract syntax. @@ -82,8 +82,8 @@ The proposal also specifies the abstract syntax. ### Expressions and Patterns -The following grammar defines the concrete syntax for -expressions. Below we comment on a few unusual aspects of the grammar. +The following grammar defines the concrete syntax for expressions. Below we +comment on a few unusual aspects of the grammar. ```Bison pattern: @@ -137,9 +137,9 @@ is for pattern variables. For example, in a variable definition such as var Int: x = 0; -the `Int: x` is parsed with the grammar rule for pattern variables. -In the right-hand side of the above grammar rule, the `expression` to -the left of the `:` must evaluate to a type at compile time. +the `Int: x` is parsed with the grammar rule for pattern variables. In the +right-hand side of the above grammar rule, the `expression` to the left of the +`:` must evaluate to a type at compile time. The grammar rule @@ -147,9 +147,9 @@ The grammar rule tuple: '(' field_list ')' ``` -is primarily for constructing a tuple, but it is also used for -creating tuple types and tuple patterns, depending on the context in -which the expression occurs. +is primarily for constructing a tuple, but it is also used for creating tuple +types and tuple patterns, depending on the context in which the expression +occurs. Regarding the grammar rule for a named argument @@ -158,11 +158,10 @@ field: '.' identifier '=' pattern ``` the period is needed to aid integrated development environments with -auto-completion. The issue is that without the period, when the -programmer is typing the identifier (and not yet typed the `=`), the -IDE doesn't know whether the identifier is for a positional argument, -in which case it is a variable occurrence, or whether the identifier is -a field name. +auto-completion. The issue is that without the period, when the programmer is +typing the identifier (and not yet typed the `=`), the IDE doesn't know whether +the identifier is for a positional argument, in which case it is a variable +occurrence, or whether the identifier is a field name. ### Statements @@ -234,11 +233,10 @@ In the grammar rule for function definitions declaration: "fn" identifier tuple return_type '{' statement_list '}' ``` -the `tuple` is used as a pattern to describe the parameters of the -function whereas the `expression` in the `return_type` must evaluate -to a type at compile-time. The grammar for function definitions does -not currently include implicit parameters, but the intent is to add -them to the grammar in the future. +the `tuple` is used as a pattern to describe the parameters of the function +whereas the `expression` in the `return_type` must evaluate to a type at +compile-time. The grammar for function definitions does not currently include +implicit parameters, but the intent is to add them to the grammar in the future. In the rule for field declarations @@ -246,18 +244,16 @@ In the rule for field declarations member: "var" expression ':' identifier ';' ``` -the `expression` must evaluate to a type at compile time. -The same is true for the `tuple` in the grammar -rule for an alternative: +the `expression` must evaluate to a type at compile time. The same is true for +the `tuple` in the grammar rule for an alternative: alternative: identifier tuple ';' ### Precedence and Associativity -The following defines the precedence and associativity of symbols -used in the grammar. The ordering is from lowest to highest -precedence. The main goal of the choices here is to stay close to -C++. +The following defines the precedence and associativity of symbols used in the +grammar. The ordering is from lowest to highest precedence. The main goal of the +choices here is to stay close to C++. nonassoc '{' '}' nonassoc ':' ',' @@ -273,12 +269,11 @@ For more information on operators and precedence, see proposal ### Abstract Syntax -The output of parsing is an abstract syntax tree. There are many ways -to define abstract syntax. Here I'll simply use C-style `struct` -definitions. The definition of the abstract syntax is important -because it is used in the specification of the semantic analysis and -the specification of runtime behavior. - +The output of parsing is an abstract syntax tree. There are many ways to define +abstract syntax. Here I'll simply use C-style `struct` definitions. The +definition of the abstract syntax is important because it is used in the +specification of the semantic analysis and the specification of runtime +behavior. #### Abstract Syntax for Expressions @@ -308,10 +303,9 @@ struct Expression { }; ``` -The correspondence between most of the grammar rules and the abstract -syntax is straightforward. However, the parsing the `field_list` -deserves some explanation. The fields can be labeled with the grammar -rule: +The correspondence between most of the grammar rules and the abstract syntax is +straightforward. However, the parsing the `field_list` deserves some +explanation. The fields can be labeled with the grammar rule: ```Bison field: '.' identifier '=' pattern @@ -323,8 +317,8 @@ or unlabeled, with the rule field: pattern ``` -The unlabeled fields are given numbers (represented as strings) for -field labels, starting with 0 and going up from left to right. +The unlabeled fields are given numbers (represented as strings) for field +labels, starting with 0 and going up from left to right. Regarding the rule for tuples: @@ -332,11 +326,9 @@ Regarding the rule for tuples: tuple: '(' field_list ')' ``` -if the field list only has a single unlabeled item without a trailing -comma, then the parse result for that item is returned. Otherwise a -`tuple` AST node is created containing the parse results for the -fields. - +if the field list only has a single unlabeled item without a trailing comma, +then the parse result for that item is returned. Otherwise a `tuple` AST node is +created containing the parse results for the fields. #### Abstract Syntax for Statements @@ -406,35 +398,32 @@ struct Declaration { ## Alternatives considered -Expressions, type expressions, and patterns could instead be defined -using completely disjoint grammar productions, but that would require -adding more keywords or symbols to avoid ambiguity and there would be -a fair amount of repetition in grammar productions. - -In this proposal, assignment is a statement. It could instead be an -expression as it is in C and C++. The arguments against -assignment-as-an-expression include 1) it complicates reasoning about -the ordering of side-effects and 2) it can cause confusion between `=` -and `==` [(SEI CERT C Coding -Standard)](https://wiki.sei.cmu.edu/confluence/display/c/EXP45-C.+Do+not+perform+assignments+in+selection+statements) -[Visual Studio -Warning](https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706?view=vs-2019). - -Inside a `choice`, an `alternative` could instead begin with a keyword -such as `alt`, `fn`, or `var`, as discussed in the proposal for -sum types [#157](https://github.com/carbon-language/carbon-lang/pull/157). - -The precedence for `and` and `or` are equal, whereas in C++ `&&` has -higher precedence than `||`. - -The parameters of a function are described using the syntax of a -pattern. There could instead be separate syntax that is special to -function parameters, as there is in C++. - -The parameters of a function type are described using the syntax for -type expression. There could instead be separate syntax that is -special to the parameters of a function type, as there is in C++. - -The `pattern` non-terminal is defined in terms of `expression`. It -could instead be the other way around, defining `expression` in terms -of `pattern`. +Expressions, type expressions, and patterns could instead be defined using +completely disjoint grammar productions, but that would require adding more +keywords or symbols to avoid ambiguity and there would be a fair amount of +repetition in grammar productions. + +In this proposal, assignment is a statement. It could instead be an expression +as it is in C and C++. The arguments against assignment-as-an-expression +include 1) it complicates reasoning about the ordering of side-effects and 2) it +can cause confusion between `=` and `==` +[(SEI CERT C Coding Standard)](https://wiki.sei.cmu.edu/confluence/display/c/EXP45-C.+Do+not+perform+assignments+in+selection+statements) +[Visual Studio Warning](https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706?view=vs-2019). + +Inside a `choice`, an `alternative` could instead begin with a keyword such as +`alt`, `fn`, or `var`, as discussed in the proposal for sum types +[#157](https://github.com/carbon-language/carbon-lang/pull/157). + +The precedence for `and` and `or` are equal, whereas in C++ `&&` has higher +precedence than `||`. + +The parameters of a function are described using the syntax of a pattern. There +could instead be separate syntax that is special to function parameters, as +there is in C++. + +The parameters of a function type are described using the syntax for type +expression. There could instead be separate syntax that is special to the +parameters of a function type, as there is in C++. + +The `pattern` non-terminal is defined in terms of `expression`. It could instead +be the other way around, defining `expression` in terms of `pattern`. diff --git a/proposals/semantic-analysis.md~ b/proposals/semantic-analysis.md~ new file mode 100644 index 0000000000000..0c26a7c755b07 --- /dev/null +++ b/proposals/semantic-analysis.md~ @@ -0,0 +1,61 @@ +# TODO + + + +[Pull request](https://github.com/carbon-language/carbon-lang/pull/####) + +## Table of contents + + + +- [TODO: Initial proposal setup](#todo-initial-proposal-setup) +- [Problem](#problem) +- [Background](#background) +- [Proposal](#proposal) +- [Details](#details) +- [Alternatives considered](#alternatives-considered) + + + +## TODO: Initial proposal setup + +> TIP: Run `../src/scripts/new_proposal.py "TITLE"` to do new proposal setup. + +1. Copy this template to `new.md`, and create a commit. +2. Create a GitHub pull request, to get a pull request number. + - Add the `proposal` and `WIP` labels to the pull request. +3. Rename `new.md` to `/proposals/p####.md`, where `####` should be the pull + request number. +4. Update the title of the proposal (the `TODO` on line 1). +5. Update the link to the pull request (the `####` on line 11). +6. Delete this section. + +TODOs indicate where content should be updated for a proposal. See +[Carbon Governance and Evolution](/docs/project/evolution.md) for more details. + +## Problem + +TODO: What problem are you trying to solve? How important is that problem? Who +is impacted by it? + +## Background + +TODO: Is there any background that readers should consider to fully understand +this problem and your approach to solving it? + +## Proposal + +TODO: Briefly and at a high level, how do you propose to solve the problem? Why +will that in fact solve it? + +## Details + +TODO: Fully explain the details of the proposed solution. + +## Alternatives considered + +TODO: What alternative solutions have you considered? From 9555b53befa645eb17f095adc686225fb667dc11 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 7 Oct 2020 14:38:34 -0400 Subject: [PATCH 37/56] changes based on meeting today --- proposals/p0162.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 18109b8fec339..e341547838baf 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -107,7 +107,7 @@ expression: | '-' expression | expression tuple | "auto" -| "fn" tuple return_type +| "fnty" tuple return_type ; tuple: '(' field_list ')' @@ -403,6 +403,9 @@ completely disjoint grammar productions, but that would require adding more keywords or symbols to avoid ambiguity and there would be a fair amount of repetition in grammar productions. +The proposal does not include declarations for uninitialized variables, leaving +that to a later proposal. + In this proposal, assignment is a statement. It could instead be an expression as it is in C and C++. The arguments against assignment-as-an-expression include 1) it complicates reasoning about the ordering of side-effects and 2) it @@ -410,9 +413,17 @@ can cause confusion between `=` and `==` [(SEI CERT C Coding Standard)](https://wiki.sei.cmu.edu/confluence/display/c/EXP45-C.+Do+not+perform+assignments+in+selection+statements) [Visual Studio Warning](https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706?view=vs-2019). +The syntax for variable initialization uses `=`, which is the same token used in +assignment. An alternative would be to use a different syntax such as `:=` to +better signal the semantic differences between initialization and assignment. +The author does not have a preference on this choice. The `:=` alternative does +not introduce any complications regarding ambiguity despite the other uses of +`:` in the grammar. + Inside a `choice`, an `alternative` could instead begin with a keyword such as `alt`, `fn`, or `var`, as discussed in the proposal for sum types -[#157](https://github.com/carbon-language/carbon-lang/pull/157). +[#157](https://github.com/carbon-language/carbon-lang/pull/157). The author does +not have a preference in this regard. The precedence for `and` and `or` are equal, whereas in C++ `&&` has higher precedence than `||`. @@ -421,9 +432,21 @@ The parameters of a function are described using the syntax of a pattern. There could instead be separate syntax that is special to function parameters, as there is in C++. +There are open questions regarding the design of function types, so we could +alternatively postpone the specification of the syntax for function types. The +choice to include them is based on the idea that Carbon will need something that +fills the roles of `std::function` and C-style function pointers. + The parameters of a function type are described using the syntax for type expression. There could instead be separate syntax that is special to the parameters of a function type, as there is in C++. +The keyword for introducing a function type is `fnty`, which is an arbitrary +choice. Other alternatives include `fn_type` and `fn`. The `fn` alternative +would conflict with future plans to use `fn` for lambda expressions. Some +dislike of `fn_type` was voiced, which motivated the choice of `fnty`. It would +be nice to do without a keyword, but as the grammar currently stands, that +introduce ambiguity. + The `pattern` non-terminal is defined in terms of `expression`. It could instead be the other way around, defining `expression` in terms of `pattern`. From 2386f28b354a58f970883e9ba57d78d2b2308d65 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 7 Oct 2020 14:57:03 -0400 Subject: [PATCH 38/56] describe alternatives regarding methods --- proposals/p0162.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index e341547838baf..df0ebd9cf24ab 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -270,7 +270,7 @@ For more information on operators and precedence, see proposal ### Abstract Syntax The output of parsing is an abstract syntax tree. There are many ways to define -abstract syntax. Here I'll simply use C-style `struct` definitions. The +abstract syntax. Here we simply use C-style `struct` definitions. The definition of the abstract syntax is important because it is used in the specification of the semantic analysis and the specification of runtime behavior. @@ -450,3 +450,13 @@ introduce ambiguity. The `pattern` non-terminal is defined in terms of `expression`. It could instead be the other way around, defining `expression` in terms of `pattern`. + +The keyword for methods in this proposal is `method`, which is one of +the choices considered in the structs proposal +[#98](https://github.com/carbon-language/carbon-lang/pull/98). That +proposal discusses the alternative of using `fn` for both function +members and method members, and differentiating between the two based +on whether the type of the first parameter is `Self`. The argument for +using `method` is that it more directly captures intent. + + From 246da2072e98e8f6eb83adb4c1c7c52aee133e34 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 7 Oct 2020 16:14:49 -0400 Subject: [PATCH 39/56] more rationale in discussion of alternatives --- proposals/p0162.md | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index df0ebd9cf24ab..73ed1b0413aa8 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -45,7 +45,7 @@ language. The grammar proposed here is based on the following proposals: - [Carbon language overview](https://github.com/carbon-language/carbon-lang/tree/trunk/docs/design) -- proposals for pattern matching +- pattern matching [#87](https://github.com/carbon-language/carbon-lang/pull/87), - structs [#98](https://github.com/carbon-language/carbon-lang/pull/98), - tuples [#111](https://github.com/carbon-language/carbon-lang/pull/111), @@ -270,10 +270,9 @@ For more information on operators and precedence, see proposal ### Abstract Syntax The output of parsing is an abstract syntax tree. There are many ways to define -abstract syntax. Here we simply use C-style `struct` definitions. The -definition of the abstract syntax is important because it is used in the -specification of the semantic analysis and the specification of runtime -behavior. +abstract syntax. Here we simply use C-style `struct` definitions. The definition +of the abstract syntax is important because it is used in the specification of +the semantic analysis and the specification of runtime behavior. #### Abstract Syntax for Expressions @@ -435,7 +434,11 @@ there is in C++. There are open questions regarding the design of function types, so we could alternatively postpone the specification of the syntax for function types. The choice to include them is based on the idea that Carbon will need something that -fills the roles of `std::function` and C-style function pointers. +fills the roles of `std::function` and C-style function pointers. Also, the +features were selected for the basic syntax proposal in a way that aimed to make +the proposal complete in the sense that each value-oriented feature (functions, +tuples, structures) comes with syntax for 1) creating values, 2) using values, +and 3) a type for classifying values. The parameters of a function type are described using the syntax for type expression. There could instead be separate syntax that is special to the @@ -451,12 +454,10 @@ introduce ambiguity. The `pattern` non-terminal is defined in terms of `expression`. It could instead be the other way around, defining `expression` in terms of `pattern`. -The keyword for methods in this proposal is `method`, which is one of -the choices considered in the structs proposal -[#98](https://github.com/carbon-language/carbon-lang/pull/98). That -proposal discusses the alternative of using `fn` for both function -members and method members, and differentiating between the two based -on whether the type of the first parameter is `Self`. The argument for -using `method` is that it more directly captures intent. - - +The keyword for methods in this proposal is `method`, which is one of the +choices considered in the structs proposal +[#98](https://github.com/carbon-language/carbon-lang/pull/98). That proposal +discusses the alternative of using `fn` for both function members and method +members, and differentiating between the two based on whether the type of the +first parameter is `Self`. The argument for using `method` is that it more +directly captures intent. From 0b6dd7abd1c3ffd8c636682a77107fbc951c5299 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 8 Oct 2020 09:56:53 -0400 Subject: [PATCH 40/56] addressing comments --- proposals/p0162.md | 28 ++++++++------- proposals/semantic-analysis.md~ | 61 --------------------------------- 2 files changed, 15 insertions(+), 74 deletions(-) delete mode 100644 proposals/semantic-analysis.md~ diff --git a/proposals/p0162.md b/proposals/p0162.md index 73ed1b0413aa8..ffbdf83741d9d 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -251,22 +251,23 @@ the `tuple` in the grammar rule for an alternative: ### Precedence and Associativity -The following defines the precedence and associativity of symbols used in the -grammar. The ordering is from lowest to highest precedence. The main goal of the -choices here is to stay close to C++. +The following precedence and associativity specification is meant to approximate +the definitions in the operators and precedence proposal +[#168](https://github.com/carbon-language/carbon-lang/pull/168) to the extent +that is possible in a `yacc`/`bison` generated parser. The ordering is from +lowest to highest precedence, with operators on the same line having equal +precedence. Proposal 168 differs in that the operator groups are partially +ordered instead of being totally ordered. nonassoc '{' '}' nonassoc ':' ',' - left "or" "and" - left "==" - left '+' '-' - right '!' '*' '&' + right "or" "and" + nonassoc "==" "not" + right '+' '-' + right '*' left '.' "->" nonassoc '(' ')' '[' ']' -For more information on operators and precedence, see proposal -[#168](https://github.com/carbon-language/carbon-lang/pull/168). - ### Abstract Syntax The output of parsing is an abstract syntax tree. There are many ways to define @@ -303,7 +304,7 @@ struct Expression { ``` The correspondence between most of the grammar rules and the abstract syntax is -straightforward. However, the parsing the `field_list` deserves some +straightforward. However, the parsing of the `field_list` deserves some explanation. The fields can be labeled with the grammar rule: ```Bison @@ -424,8 +425,9 @@ Inside a `choice`, an `alternative` could instead begin with a keyword such as [#157](https://github.com/carbon-language/carbon-lang/pull/157). The author does not have a preference in this regard. -The precedence for `and` and `or` are equal, whereas in C++ `&&` has higher -precedence than `||`. +The precedence for `and` and `or` are equal, following the suggestion of +proposal [#168](https://github.com/carbon-language/carbon-lang/pull/168), +whereas in C++ `&&` has higher precedence than `||`. The parameters of a function are described using the syntax of a pattern. There could instead be separate syntax that is special to function parameters, as diff --git a/proposals/semantic-analysis.md~ b/proposals/semantic-analysis.md~ deleted file mode 100644 index 0c26a7c755b07..0000000000000 --- a/proposals/semantic-analysis.md~ +++ /dev/null @@ -1,61 +0,0 @@ -# TODO - - - -[Pull request](https://github.com/carbon-language/carbon-lang/pull/####) - -## Table of contents - - - -- [TODO: Initial proposal setup](#todo-initial-proposal-setup) -- [Problem](#problem) -- [Background](#background) -- [Proposal](#proposal) -- [Details](#details) -- [Alternatives considered](#alternatives-considered) - - - -## TODO: Initial proposal setup - -> TIP: Run `../src/scripts/new_proposal.py "TITLE"` to do new proposal setup. - -1. Copy this template to `new.md`, and create a commit. -2. Create a GitHub pull request, to get a pull request number. - - Add the `proposal` and `WIP` labels to the pull request. -3. Rename `new.md` to `/proposals/p####.md`, where `####` should be the pull - request number. -4. Update the title of the proposal (the `TODO` on line 1). -5. Update the link to the pull request (the `####` on line 11). -6. Delete this section. - -TODOs indicate where content should be updated for a proposal. See -[Carbon Governance and Evolution](/docs/project/evolution.md) for more details. - -## Problem - -TODO: What problem are you trying to solve? How important is that problem? Who -is impacted by it? - -## Background - -TODO: Is there any background that readers should consider to fully understand -this problem and your approach to solving it? - -## Proposal - -TODO: Briefly and at a high level, how do you propose to solve the problem? Why -will that in fact solve it? - -## Details - -TODO: Fully explain the details of the proposed solution. - -## Alternatives considered - -TODO: What alternative solutions have you considered? From 4d66890169af1551598149978193b5b9249f5413 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 21 Oct 2020 12:33:44 -0400 Subject: [PATCH 41/56] typo fix, added text about next steps --- proposals/p0162.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index ffbdf83741d9d..4baf18d06b5a8 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -78,6 +78,19 @@ the next section. The proposal also specifies the abstract syntax. +When this proposal is accepted, the accompanying `flex`, `bison`, and C++ files +that define the grammar and implement the parser actions will be placed in the +`executable_semantics` directory of the `carbon-lang` repository. + +In the near future there will be proposals regarding the semantic analysis (aka. +type checking) and specification of runtime behavior for this subset of Carbon +with the plan to add the accompanying executable forms of those specifications +to the `executable_semantics` directory. + +Looking towards growing the Carbon language, the intent is for other proposals +to add to this grammar by modifying the `flex` and `bison` files and the +abstract syntax definitions in the `executable_semantics` directory. + ## Details ### Expressions and Patterns @@ -123,7 +136,7 @@ field: ; return_type: /* empty */ -| ARROW expression +| "->" expression ; ``` @@ -208,7 +221,6 @@ declaration: ; member: "var" expression ':' identifier ';' -| "method" identifier expression "->" expression '{' statement_list '}' ; member_list: /* empty */ @@ -455,11 +467,3 @@ introduce ambiguity. The `pattern` non-terminal is defined in terms of `expression`. It could instead be the other way around, defining `expression` in terms of `pattern`. - -The keyword for methods in this proposal is `method`, which is one of the -choices considered in the structs proposal -[#98](https://github.com/carbon-language/carbon-lang/pull/98). That proposal -discusses the alternative of using `fn` for both function members and method -members, and differentiating between the two based on whether the type of the -first parameter is `Self`. The argument for using `method` is that it more -directly captures intent. From 591870447b052c63476280ab55eb5d56821a054b Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 22 Oct 2020 11:24:59 -0400 Subject: [PATCH 42/56] removed *, changed a ! to not --- proposals/p0162.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 4baf18d06b5a8..1a8971d956c14 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -116,7 +116,7 @@ expression: | expression '-' expression | expression "and" expression | expression "or" expression -| '!' expression +| "not" expression | '-' expression | expression tuple | "auto" @@ -276,7 +276,6 @@ ordered instead of being totally ordered. right "or" "and" nonassoc "==" "not" right '+' '-' - right '*' left '.' "->" nonassoc '(' ')' '[' ']' From c2d963c06dfc088d51b9f14a3038a45be2fa02ef Mon Sep 17 00:00:00 2001 From: "Jeremy G. Siek" Date: Fri, 23 Oct 2020 17:49:39 -0400 Subject: [PATCH 43/56] Update proposals/p0162.md Co-authored-by: Geoff Romer --- proposals/p0162.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 1a8971d956c14..e73cfa3fbac85 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -64,11 +64,11 @@ the next section. - `statement` includes local variable definitions, assignment, blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. -- `expression` plays two roles: for value-producing expressions and for - type-producing expressions. In an initial attempt these roles were separate - but that led to ambiguities in the grammar. Folding them into one category - resolved the ambiguities. The two roles are teased apart in the type system - and semantics. +- `expression` includes function calls, arithmetic, literals, and other syntaxes + that evaluate to a value. In Carbon, a type is a kind of value, and Carbon + makes no syntactic distinction between type-valued expressions and other + kinds of expressions, so the `expression` nonterminal is used in positions + where a type is expected. - `pattern` for the patterns in a `match` statement, for the left-hand side of a variable definition, and for describing the parameters of a function. The From a76b9757a63aa1fef0c959f4afa42dbd10b2cec0 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Fri, 23 Oct 2020 17:50:41 -0400 Subject: [PATCH 44/56] edits from feedback --- proposals/p0162.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 1a8971d956c14..b402cb8463f0a 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -30,15 +30,17 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Problem -The purpose of this proposal is to establish some basic syntactic elements of -the Carbon language and make sure that the grammar is unambiguous and can be -parsed by an LALR parser such as `yacc` or `bison`. The grammar presented here -has indeed been checked by `bison`. The language features in this basic grammar -include control flow via `if` and `while`, functions, simple structures, choice, -and pattern matching. The main syntactic categories are `declaration`, -`statement`, and `expression`. Establishing these syntactic categories should -help the other proposals choose syntax that is compatible with the rest of the -language. +The purpose of this proposal is to establish some basic syntactic +elements of the Carbon language and make sure that the grammar is +unambiguous and can be parsed by an LALR parser such as `yacc` or +`bison`. The grammar presented here has indeed been checked by +`bison`. The language features in this basic grammar include control +flow via `if` and `while`, functions, simple structures, choice, +pattern matching, and a sample of operators on Booleans and +integers. The main syntactic categories are `declaration`, +`statement`, and `expression`. Establishing these syntactic categories +should help the other proposals choose syntax that is compatible with +the rest of the language. ## Background @@ -74,7 +76,9 @@ the next section. a variable definition, and for describing the parameters of a function. The grammar treats patterns and expressions identically, but the type system will only allow pattern variables (`expression ':' identifier`) in patterns - and not in value or type-producing expressions. + and not in value or type-producing expressions. Having the separate + non-terminals for `pattern` and `expression` helps to document this + distinction. The proposal also specifies the abstract syntax. From 9ca724912406ced13367105ddf16bfced45fbbc8 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Fri, 23 Oct 2020 17:52:32 -0400 Subject: [PATCH 45/56] pre-commit --- proposals/p0162.md | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 3b6625b04c59c..e5d9aaf6abb07 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -61,24 +61,27 @@ The grammar proposed here is based on the following proposals: We summarize the four main syntactic categories here and define the grammar in the next section. -- `declaration` includes function, structure, and choice definitions. - -- `statement` includes local variable definitions, assignment, blocks, `if`, - `while`, `match`, `break`, `continue`, and `return`. - -- `expression` includes function calls, arithmetic, literals, and other syntaxes - that evaluate to a value. In Carbon, a type is a kind of value, and Carbon - makes no syntactic distinction between type-valued expressions and other - kinds of expressions, so the `expression` nonterminal is used in positions - where a type is expected. - -- `pattern` for the patterns in a `match` statement, for the left-hand side of - a variable definition, and for describing the parameters of a function. The - grammar treats patterns and expressions identically, but the type system - will only allow pattern variables (`expression ':' identifier`) in patterns - and not in value or type-producing expressions. Having the separate - non-terminals for `pattern` and `expression` helps to document this - distinction. +- `declaration` includes function, structure, and choice + definitions. + +- `statement` includes local variable definitions, assignment, + blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. + +- `expression` includes function calls, arithmetic, literals, and + other syntaxes that evaluate to a value. In Carbon, a type is a + kind of value, and Carbon makes no syntactic distinction between + type-valued expressions and other kinds of expressions, so the + `expression` nonterminal is used in positions where a type is + expected. + +- `pattern` for the patterns in a `match` statement, for the + left-hand side of a variable definition, and for describing the + parameters of a function. The grammar treats patterns and + expressions identically, but the type system will only allow + pattern variables (`expression ':' identifier`) in patterns and + not in value or type-producing expressions. Having the separate + non-terminals for `pattern` and `expression` helps to document + this distinction. The proposal also specifies the abstract syntax. From df011b3b5e69c71127bcb9bceffab91bee51465f Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Fri, 23 Oct 2020 17:54:30 -0400 Subject: [PATCH 46/56] added second reason for period in field initializer --- proposals/p0162.md | 56 +++++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index e5d9aaf6abb07..5ebfcc4e70b65 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -30,17 +30,15 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception ## Problem -The purpose of this proposal is to establish some basic syntactic -elements of the Carbon language and make sure that the grammar is -unambiguous and can be parsed by an LALR parser such as `yacc` or -`bison`. The grammar presented here has indeed been checked by -`bison`. The language features in this basic grammar include control -flow via `if` and `while`, functions, simple structures, choice, -pattern matching, and a sample of operators on Booleans and -integers. The main syntactic categories are `declaration`, -`statement`, and `expression`. Establishing these syntactic categories -should help the other proposals choose syntax that is compatible with -the rest of the language. +The purpose of this proposal is to establish some basic syntactic elements of +the Carbon language and make sure that the grammar is unambiguous and can be +parsed by an LALR parser such as `yacc` or `bison`. The grammar presented here +has indeed been checked by `bison`. The language features in this basic grammar +include control flow via `if` and `while`, functions, simple structures, choice, +pattern matching, and a sample of operators on Booleans and integers. The main +syntactic categories are `declaration`, `statement`, and `expression`. +Establishing these syntactic categories should help the other proposals choose +syntax that is compatible with the rest of the language. ## Background @@ -61,27 +59,24 @@ The grammar proposed here is based on the following proposals: We summarize the four main syntactic categories here and define the grammar in the next section. -- `declaration` includes function, structure, and choice - definitions. +- `declaration` includes function, structure, and choice definitions. -- `statement` includes local variable definitions, assignment, - blocks, `if`, `while`, `match`, `break`, `continue`, and `return`. +- `statement` includes local variable definitions, assignment, blocks, `if`, + `while`, `match`, `break`, `continue`, and `return`. -- `expression` includes function calls, arithmetic, literals, and - other syntaxes that evaluate to a value. In Carbon, a type is a - kind of value, and Carbon makes no syntactic distinction between - type-valued expressions and other kinds of expressions, so the - `expression` nonterminal is used in positions where a type is - expected. +- `expression` includes function calls, arithmetic, literals, and other + syntaxes that evaluate to a value. In Carbon, a type is a kind of value, and + Carbon makes no syntactic distinction between type-valued expressions and + other kinds of expressions, so the `expression` nonterminal is used in + positions where a type is expected. -- `pattern` for the patterns in a `match` statement, for the - left-hand side of a variable definition, and for describing the - parameters of a function. The grammar treats patterns and - expressions identically, but the type system will only allow - pattern variables (`expression ':' identifier`) in patterns and - not in value or type-producing expressions. Having the separate - non-terminals for `pattern` and `expression` helps to document - this distinction. +- `pattern` for the patterns in a `match` statement, for the left-hand side of + a variable definition, and for describing the parameters of a function. The + grammar treats patterns and expressions identically, but the type system + will only allow pattern variables (`expression ':' identifier`) in patterns + and not in value or type-producing expressions. Having the separate + non-terminals for `pattern` and `expression` helps to document this + distinction. The proposal also specifies the abstract syntax. @@ -181,7 +176,8 @@ the period is needed to aid integrated development environments with auto-completion. The issue is that without the period, when the programmer is typing the identifier (and not yet typed the `=`), the IDE doesn't know whether the identifier is for a positional argument, in which case it is a variable -occurrence, or whether the identifier is a field name. +occurrence, or whether the identifier is a field name. The period also enables +the addition of new keywords to Carbon without colliding with field names. ### Statements From e62dac30796cb93db3f2bc7bcf28fb1734ac6ada Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 24 Oct 2020 09:33:02 -0400 Subject: [PATCH 47/56] added executable semantics, fixed misunderstanding regarding associativity --- executable-semantics/Makefile | 51 + executable-semantics/README.md | 148 ++ executable-semantics/assoc_list.h | 33 + executable-semantics/ast.cc | 653 +++++++ executable-semantics/ast.h | 238 +++ executable-semantics/cons_list.h | 26 + executable-semantics/examples/block1.6c | 7 + executable-semantics/examples/break1.6c | 10 + executable-semantics/examples/choice1.6c | 28 + executable-semantics/examples/continue1.6c | 9 + executable-semantics/examples/fun-recur.6c | 10 + executable-semantics/examples/fun1.6c | 7 + executable-semantics/examples/fun2.6c | 14 + executable-semantics/examples/fun3.6c | 8 + executable-semantics/examples/fun4.6c | 7 + executable-semantics/examples/fun5.6c | 5 + executable-semantics/examples/funptr1.6c | 8 + .../examples/match-int-default.6c | 11 + executable-semantics/examples/match-int.6c | 9 + executable-semantics/examples/match-type.6c | 17 + executable-semantics/examples/next.6c | 5 + executable-semantics/examples/pattern-init.6c | 4 + executable-semantics/examples/record1.6c | 5 + executable-semantics/examples/struct1.6c | 9 + executable-semantics/examples/struct2.6c | 11 + executable-semantics/examples/struct3.6c | 8 + executable-semantics/examples/tuple-assign.6c | 6 + executable-semantics/examples/tuple-match.6c | 7 + executable-semantics/examples/tuple1.6c | 6 + executable-semantics/examples/tuple2.6c | 4 + executable-semantics/examples/undef1.6c | 14 + executable-semantics/examples/undef2.6c | 9 + executable-semantics/examples/while1.6c | 6 + executable-semantics/examples/zero.6c | 3 + executable-semantics/interp.cc | 1704 +++++++++++++++++ executable-semantics/interp.h | 131 ++ executable-semantics/syntax.l | 77 + executable-semantics/syntax.y | 324 ++++ executable-semantics/typecheck.cc | 862 +++++++++ executable-semantics/typecheck.h | 50 + proposals/p0162.md | 10 +- 41 files changed, 4549 insertions(+), 5 deletions(-) create mode 100644 executable-semantics/Makefile create mode 100644 executable-semantics/README.md create mode 100644 executable-semantics/assoc_list.h create mode 100644 executable-semantics/ast.cc create mode 100644 executable-semantics/ast.h create mode 100644 executable-semantics/cons_list.h create mode 100644 executable-semantics/examples/block1.6c create mode 100644 executable-semantics/examples/break1.6c create mode 100644 executable-semantics/examples/choice1.6c create mode 100644 executable-semantics/examples/continue1.6c create mode 100644 executable-semantics/examples/fun-recur.6c create mode 100644 executable-semantics/examples/fun1.6c create mode 100644 executable-semantics/examples/fun2.6c create mode 100644 executable-semantics/examples/fun3.6c create mode 100644 executable-semantics/examples/fun4.6c create mode 100644 executable-semantics/examples/fun5.6c create mode 100644 executable-semantics/examples/funptr1.6c create mode 100644 executable-semantics/examples/match-int-default.6c create mode 100644 executable-semantics/examples/match-int.6c create mode 100644 executable-semantics/examples/match-type.6c create mode 100644 executable-semantics/examples/next.6c create mode 100644 executable-semantics/examples/pattern-init.6c create mode 100644 executable-semantics/examples/record1.6c create mode 100644 executable-semantics/examples/struct1.6c create mode 100644 executable-semantics/examples/struct2.6c create mode 100644 executable-semantics/examples/struct3.6c create mode 100644 executable-semantics/examples/tuple-assign.6c create mode 100644 executable-semantics/examples/tuple-match.6c create mode 100644 executable-semantics/examples/tuple1.6c create mode 100644 executable-semantics/examples/tuple2.6c create mode 100644 executable-semantics/examples/undef1.6c create mode 100644 executable-semantics/examples/undef2.6c create mode 100644 executable-semantics/examples/while1.6c create mode 100644 executable-semantics/examples/zero.6c create mode 100644 executable-semantics/interp.cc create mode 100644 executable-semantics/interp.h create mode 100644 executable-semantics/syntax.l create mode 100644 executable-semantics/syntax.y create mode 100644 executable-semantics/typecheck.cc create mode 100644 executable-semantics/typecheck.h diff --git a/executable-semantics/Makefile b/executable-semantics/Makefile new file mode 100644 index 0000000000000..e4e71b85f0b7a --- /dev/null +++ b/executable-semantics/Makefile @@ -0,0 +1,51 @@ +a.out: syntax.l syntax.y ast.h typecheck.h interp.h ast.cc typecheck.cc interp.cc cons_list.h assoc_list.h + bison -d syntax.y + flex syntax.l + bison -v --debug syntax.y -o syntax.tab.cc + g++ -std=c++11 -Wno-deprecated-register -c -g lex.yy.c + g++ -std=c++11 -c -g ast.cc + g++ -std=c++11 -c -g typecheck.cc + g++ -std=c++11 -c -g interp.cc + g++ -std=c++11 -g lex.yy.o ast.o typecheck.o interp.o syntax.tab.cc + +# TODO: replace this with a script that can handle +# tests that are suppose to produce errors. +test: + ./a.out examples/zero.6c + ./a.out examples/next.6c + ./a.out examples/while1.6c + ./a.out examples/fun1.6c + ./a.out examples/fun2.6c + ./a.out examples/fun3.6c + ./a.out examples/fun4.6c + ./a.out examples/fun5.6c + ./a.out examples/fun-recur.6c + ./a.out examples/funptr1.6c + ./a.out examples/block1.6c + ./a.out examples/break1.6c + ./a.out examples/continue1.6c + ./a.out examples/struct1.6c + ./a.out examples/struct2.6c + ./a.out examples/choice1.6c + ./a.out examples/tuple1.6c + ./a.out examples/tuple2.6c + ./a.out examples/tuple-match.6c + ./a.out examples/tuple-assign.6c + ./a.out examples/record1.6c + ./a.out examples/match-int.6c + ./a.out examples/match-int-default.6c + ./a.out examples/match-type.6c + ./a.out examples/pattern-init.6c + ./a.out examples/struct3.6c + +clean: + rm -f a.out + rm -f *.o + rm -f syntax.tab.h + rm -f syntax.tab.cc syntax.tab.c syntax.output + rm -f lex.yy.c lex.yy.cc + rm -f a.out + rm -rf a.out.dSYM + rm -f log + rm -f *~ + rm -f examples/*~ diff --git a/executable-semantics/README.md b/executable-semantics/README.md new file mode 100644 index 0000000000000..05ad154f9b9f9 --- /dev/null +++ b/executable-semantics/README.md @@ -0,0 +1,148 @@ +# Executable Semantics + +This directory contains a work-in-progress executable semantics. It +started as an executable semantics for Featherweight C and it is +migrating into an executable semantics for the Carbon language. It +includes a parser, type checker, and abstract machine. + +This language currently includes several kinds of values: integer, +booleans, pointers, functions, and structs. A kind of safe union, +called a `choice`, is in progress. Regarding control-flow, it includes +if statements, while loops, break, continue, function calls, and a +variant of `switch` called `match` is in progress. + +The parser is implemented using the flex and bison parser generator +tools. + +* [`syntax.l`](./syntax.l) the lexer specification +* [`syntax.y`](./syntax.y) the grammar + +The parser translates program text into an abstract syntax tree (AST). + +* [`ast.h`](./ast.h) includes structure definitions for the AST and function + declarations for creating and printing ASTs. +* [`ast.cc`](./ast.cc) contains the function definitions. + +The type checker defines what it means for an AST to be a valid +program. The type checker prints an error and exits if the AST is +invalid. + +* [`typecheck.h`](./typecheck.h) +* [`typecheck.cc`](./typecheck.cc) + +The parser and type checker together specify the static (compile-time) +semantics. + +The dynamic (run-time) semantics is specified by an abstract +machine. Abstract machines have several positive characteristics that +make them good for specification: + +* abstract machines operate on the AST of the program + (and not some lower-level representation such as bytecode) + so they directly connect the program to its behavior + +* abstract machines can easily handle language features with complex + control-flow, such as goto, exceptions, coroutines, and even + first-class continuations. + +The one down-side of abstract machines is that they are not as simple +as a definitional interpreter (a recursive function that interprets +the program), but it is more difficult to handle complex control flow in +a definitional interpreter. + +* [`interp.h`](./interp.h) declares the `interp_program` function. +* [`interp.cc`](./interp.cc) implements `interp_program` function using an + abstract machine, as described below. + +The abstract machine implements a state-transition system. The state +is defined by the `State` structure, which includes three components: +the procedure call stack, the heap, and the function definitions. The +`step` function updates the state by executing a little bit of the +program. The `step` function is called repeatedly to execute the +entire program. + +An implementation of the language (such as a compiler) must be +observationally equivalent to this abstract machine. The notion of +observation is different for each language, and can include things +like input and output. This language is currently so simple that the +only thing that is observable is the final result, an integer. So an +implementation must produce the same final result as the one produces +by the abstract machine. In particular, an implementation does **not** +have to mimic each step of the abstract machine and does not have to +use the same kinds of data structures to store the state of the +program. + +A procedure call frame, defined by the `Frame` structure, includes a +pointer to the function being called, the environment that maps +variables to their addresses, and a to-do list of actions. Each +action corresponds to an expression or statement in the program. The +`Act` structure represents an action. An action often spawns other +actions that needs to be completed first and afterwards uses their +results to complete its action. To keep track of this process, each +action includes a position field `pos` that stores an integer that +starts at `-1` and increments as the action makes progress. For +example, suppose the action associated with an addition expression +`e1 + e2` as at the top of the to-do list: + + (e1 + e2) [-1] :: ... + +When this action kicks off (in the `step_exp` function), it increments +`pos` to `0` and pushes `e1` onto the to-do list, so the top of the +todo list now looks like: + + e1 [-1] :: (e1 + e2) [0] :: ... + +Skipping over the processing of `e1`, it eventually turns into +an integer value `n1`: + + n1 :: (e1 + e2) [0] + +Because there is a value at the top of the to-do list, the `step` +function invokes `handle_value` which then dispatches on the next +action on the to-do list, in this case the addition. The addition +action spawns an action for subexpression `e2`, increments +`pos` to `1`, and remembers `n1`. + + e2 [-1] :: (e1 + e2) [1](n1) :: ... + +Skipping over the processing of `e2`, it eventually turns into +an integer value `n2`: + + n2 :: (e1 + e2) [1](n1) :: ... + +Again the `step` function invokes `handle_value` and dispatches to the +addition action which performs the arithmetic and pushes the result on +the to-do list. Let `n3` be the sum of `n1` and `n2`. + + n3 :: ... + +The heap is an array of values. It is used not only for `malloc` but +also to store anything that is mutable, including function parameters +and local variables. A pointer is simply an index into the array. +The `malloc` expression causes the heap to grow (at the end) and +returns the index of the last slot. The dereference expression +returns the nth value of the heap, as specified by the dereferenced +pointer. The assignment operation stores the value of the right-hand +side into the heap at the index specified by the left-hand side +lvalue. + +As you might expect, function calls push a new frame on the stack and +the `return` statement pops a frame off the stack. The parameter +passing semantics is call-by-value, so the machine applies `copy_val` +to the incoming arguments and the outgoing return value. Also, the +machine is careful to kill the parameters and local variables when the +function call is complete. + +The [`examples/`](./examples/) subdirectory includes some example programs. + + +## To-do List + +* scoping of function parameters wrt. other parameters +* generics with interfaces +* nested exhaustiveness checking +* multi-pattern functions + unless return type is empty tuple, in which case, insert + a return of an empty tuple. Or introduce `void`? +* function overloading +* pretty printing with indentation diff --git a/executable-semantics/assoc_list.h b/executable-semantics/assoc_list.h new file mode 100644 index 0000000000000..f21ad0855a94f --- /dev/null +++ b/executable-semantics/assoc_list.h @@ -0,0 +1,33 @@ +#ifndef ASSOC_LIST_H +#define ASSOC_LIST_H + +#include +#include +using std::cerr; +using std::endl; + +template +struct AList { + K key; + V value; + AList* next; + AList(K k, V v, AList* n) : key(k), value(v), next(n) { } +}; + +template +V lookup(int lineno, AList* alist, K key, void (*print_key)(K)) { + if (alist == NULL) { + cerr << lineno << ": could not find `"; + print_key(key); + cerr << "`" << endl; + exit(-1); + } else if (alist->key == key) { + return alist->value; + } else { + return lookup(lineno, alist->next, key, print_key); + } +} + + + +#endif diff --git a/executable-semantics/ast.cc b/executable-semantics/ast.cc new file mode 100644 index 0000000000000..ffe906cd372bc --- /dev/null +++ b/executable-semantics/ast.cc @@ -0,0 +1,653 @@ +#include +#include +#include +#include +#include "ast.h" +#include "interp.h" + +using std::cout; +using std::endl; +using std::make_pair; + +/***** Utilities *****/ + +char* input; + +/***** Types *****/ + +Expression* make_type_type(int lineno) { + Expression* t = new Expression(); + t->tag = TypeT; + t->lineno = lineno; + return t; +} + +Expression* make_int_type(int lineno) { + Expression* t = new Expression(); + t->tag = IntT; + t->lineno = lineno; + return t; +} + +Expression* make_bool_type(int lineno) { + Expression* t = new Expression(); + t->tag = BoolT; + t->lineno = lineno; + return t; +} + +Expression* make_auto_type(int lineno) { + Expression* t = new Expression(); + t->tag = AutoT; + t->lineno = lineno; + return t; +} + +Expression* make_fun_type(int lineno, Expression* param, Expression* ret) { + Expression* t = new Expression(); + t->tag = FunctionT; + t->lineno = lineno; + t->u.function_type.parameter = param; + t->u.function_type.return_type = ret; + return t; +} + +void print_string(string* s) { + cout << *s; +} + +/***** Expressions *****/ + +Expression* make_var(int lineno, string var) { + Expression* v = new Expression(); + v->lineno = lineno; + v->tag = Variable; + v->u.variable.name = new string(var); + return v; +} + +Expression* make_var_pat(int lineno, string var, Expression* type) { + Expression* v = new Expression(); + v->lineno = lineno; + v->tag = PatternVariable; + v->u.pattern_variable.name = new string(var); + v->u.pattern_variable.type = type; + return v; +} + +Expression* make_int(int lineno, int i) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = Integer; + e->u.integer = i; + return e; +} + +Expression* make_bool(int lineno, bool b) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = Boolean; + e->u.boolean = b; + return e; +} + +Expression* make_op(int lineno, enum Operator op, vector* args) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = PrimitiveOp; + e->u.primitive_op.operator_ = op; + e->u.primitive_op.arguments = args; + return e; +} + +Expression* make_unop(int lineno, enum Operator op, Expression* arg) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = PrimitiveOp; + e->u.primitive_op.operator_ = op; + vector* args = new vector(); + args->push_back(arg); + e->u.primitive_op.arguments = args; + return e; +} + +Expression* make_binop(int lineno, enum Operator op, Expression* arg1, Expression* arg2) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = PrimitiveOp; + e->u.primitive_op.operator_ = op; + vector* args = new vector(); + args->push_back(arg1); + args->push_back(arg2); + e->u.primitive_op.arguments = args; + return e; +} + +Expression* make_call(int lineno, Expression* fun, Expression* arg) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = Call; + e->u.call.function = fun; + e->u.call.argument = arg; + return e; +} + +Expression* make_get_field(int lineno, Expression* exp, string field) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = GetField; + e->u.get_field.aggregate = exp; + e->u.get_field.field = new string(field); + return e; +} + +Expression* make_tuple(int lineno, vector >* args) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = Tuple; + int i = 0; + for (auto f = args->begin(); f != args->end(); ++f) { + if (f->first == "") { + f->first = std::to_string(i); + ++i; + } + } + e->u.tuple.fields = args; + return e; +} + +Expression* make_index(int lineno, Expression* exp, Expression* i) { + Expression* e = new Expression(); + e->lineno = lineno; + e->tag = Index; + e->u.index.aggregate = exp; + e->u.index.offset = i; + return e; +} + +void print_op(Operator op) { + switch (op) { + case Neg: + cout << "-"; + break; + case Add: + cout << "+"; + break; + case Sub: + cout << "-"; + break; + case Not: + cout << "!"; + break; + case And: + cout << "&&"; + break; + case Or: + cout << "||"; + break; + case Eq: + cout << "=="; + break; + } +} + +void print_fields(vector >* fields) { + int i = 0; + for (auto iter = fields->begin(); iter != fields->end(); ++iter, ++i) { + if (i != 0) + cout << ", "; + cout << iter->first; + cout << " = "; + print_exp(iter->second); + } +} + +void print_exp(Expression* e) { + switch (e->tag) { + case Index: + print_exp(e->u.index.aggregate); + cout << "["; + print_exp(e->u.index.offset); + cout << "]"; + break; + case GetField: + print_exp(e->u.get_field.aggregate); + cout << "."; + cout << * e->u.get_field.field; + break; + case Tuple: + cout << "("; + print_fields(e->u.tuple.fields); + cout << ")"; + break; + case Integer: + cout << e->u.integer; + break; + case Boolean: + cout << std::boolalpha; + cout << e->u.boolean; + break; + case PrimitiveOp: + cout << "("; + if (e->u.primitive_op.arguments->size() == 0) { + print_op(e->u.primitive_op.operator_); + } else if (e->u.primitive_op.arguments->size() == 1) { + print_op(e->u.primitive_op.operator_); + cout << " "; + auto iter = e->u.primitive_op.arguments->begin(); + print_exp(*iter); + } else if (e->u.primitive_op.arguments->size() == 2) { + auto iter = e->u.primitive_op.arguments->begin(); + print_exp(*iter); + cout << " "; + print_op(e->u.primitive_op.operator_); + cout << " "; + ++iter; + print_exp(*iter); + } + cout << ")"; + break; + case Variable: + cout << * e->u.variable.name; + break; + case PatternVariable: + print_exp(e->u.pattern_variable.type); + cout << ": "; + cout << * e->u.pattern_variable.name; + break; + case Call: + print_exp(e->u.call.function); + if (e->u.call.argument->tag == Tuple) { + print_exp(e->u.call.argument); + } else { + cout << "("; + print_exp(e->u.call.argument); + cout << ")"; + } + break; + case BoolT: + cout << "Bool"; + break; + case IntT: + cout << "Int"; + break; + case TypeT: + cout << "Type"; + break; + case AutoT: + cout << "auto"; + break; + case FunctionT: + cout << "fn "; + print_exp(e->u.function_type.parameter); + cout << " -> "; + print_exp(e->u.function_type.return_type); + break; + } +} + +/***** Expression or Field List *****/ + +ExpOrFieldList* make_exp(Expression* exp) { + auto e = new ExpOrFieldList(); + e->tag = Exp; + e->u.exp = exp; + return e; +} + +ExpOrFieldList* make_field_list(list >* fields) { + auto e = new ExpOrFieldList(); + e->tag = FieldList; + e->u.fields = fields; + return e; +} + +ExpOrFieldList* cons_field(ExpOrFieldList* e1, ExpOrFieldList* e2) { + auto fields = new list >(); + switch (e1->tag) { + case Exp: + fields->push_back(make_pair("", e1->u.exp)); + break; + case FieldList: + for (auto i = e1->u.fields->begin(); i != e1->u.fields->end(); ++i) { + fields->push_back(*i); + } + break; + } + switch (e2->tag) { + case Exp: + fields->push_back(make_pair("", e2->u.exp)); + break; + case FieldList: + for (auto i = e2->u.fields->begin(); i != e2->u.fields->end(); ++i) { + fields->push_back(*i); + } + break; + } + return make_field_list(fields); +} + +/***** Statements *****/ + +Statement* make_exp_stmt(int lineno, Expression* exp) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = ExpressionStatement; + s->u.exp = exp; + return s; +} + +Statement* make_assign(int lineno, Expression* lhs, Expression* rhs) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Assign; + s->u.assign.lhs = lhs; + s->u.assign.rhs = rhs; + return s; +} + +Statement* make_var_def(int lineno, Expression* pat, Expression* init) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = VariableDefinition; + s->u.variable_definition.pat = pat; + s->u.variable_definition.init = init; + return s; +} + +Statement* make_if(int lineno, Expression* cond, Statement* thn, Statement* els) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = If; + s->u.if_stmt.cond = cond; + s->u.if_stmt.thn = thn; + s->u.if_stmt.els = els; + return s; +} + +Statement* make_while(int lineno, Expression* cond, Statement* body) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = While; + s->u.while_stmt.cond = cond; + s->u.while_stmt.body = body; + return s; +} + +Statement* make_break(int lineno) { + cout << "make_block" << endl; + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Break; + return s; +} + +Statement* make_continue(int lineno) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Continue; + return s; +} + +Statement* make_return(int lineno, Expression* e) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Return; + s->u.return_stmt = e; + return s; +} + +Statement* make_seq(int lineno, Statement* s1, Statement* s2) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Sequence; + s->u.sequence.stmt = s1; + s->u.sequence.next = s2; + return s; +} + +Statement* make_block(int lineno, Statement* stmt) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Block; + s->u.block.stmt = stmt; + return s; +} + +Statement* make_match(int lineno, Expression* exp, list< pair >* clauses) { + Statement* s = new Statement(); + s->lineno = lineno; + s->tag = Match; + s->u.match_stmt.exp = exp; + s->u.match_stmt.clauses = clauses; + return s; +} + +void print_stmt(Statement* s, int depth) { + if (! s) + return; + if (depth == 0) { + cout << " ... "; + return; + } + switch (s->tag) { + case Match: + cout << "match ("; + print_exp(s->u.match_stmt.exp); + cout << ") {"; + if (depth < 0 || depth > 1) { + cout << endl; + for (auto c = s->u.match_stmt.clauses->begin(); + c != s->u.match_stmt.clauses->end(); ++c) { + cout << "case "; + print_exp(c->first); + cout << " =>" << endl; + print_stmt(c->second, depth - 1); + cout << endl; + } + } else { + cout << "..."; + } + cout << "}"; + break; + case While: + cout << "while ("; + print_exp(s->u.while_stmt.cond); + cout << ")" << endl; + print_stmt(s->u.while_stmt.body, depth - 1); + break; + case Break: + cout << "break;"; + break; + case Continue: + cout << "continue;"; + break; + case VariableDefinition: + cout << "var "; + print_exp(s->u.variable_definition.pat); + cout << " = "; + print_exp(s->u.variable_definition.init); + cout << ";"; + break; + case ExpressionStatement: + print_exp(s->u.exp); + cout << ";"; + break; + case Assign: + print_exp(s->u.assign.lhs); + cout << " = "; + print_exp(s->u.assign.rhs); + cout << ";"; + break; + case If: + cout << "if ("; + print_exp(s->u.if_stmt.cond); + cout << ")" << endl; + print_stmt(s->u.if_stmt.thn, depth - 1); + cout << endl << "else" << endl; + print_stmt(s->u.if_stmt.els, depth - 1); + break; + case Return: + cout << "return "; + print_exp(s->u.return_stmt); + cout << ";"; + break; + case Sequence: + print_stmt(s->u.sequence.stmt, depth); + if (depth < 0 || depth > 1) + cout << endl; + print_stmt(s->u.sequence.next, depth - 1); + break; + case Block: + cout << "{" << endl; + print_stmt(s->u.block.stmt, depth - 1); + cout << endl << "}" << endl; + } +} + +/***** Struct Members *****/ + +Member* make_field(int lineno, string name, Expression* type) { + auto m = new Member(); + m->lineno = lineno; + m->tag = FieldMember; + m->u.field.name = new string(name); + m->u.field.type = type; + return m; +} + +/***** Declarations *****/ + +struct FunctionDefinition* +make_fun_def(int lineno, string name, Expression* ret_type, + Expression* param_pattern, Statement* body) { + struct FunctionDefinition* f = new struct FunctionDefinition(); + f->lineno = lineno; + f->name = name; + f->return_type = ret_type; + f->param_pattern = param_pattern; + f->body = body; + return f; +} + +Declaration* make_fun_decl(struct FunctionDefinition* f) { + Declaration* d = new Declaration(); + d->tag = FunctionDeclaration; + d->u.fun_def = f; + return d; +} + +Declaration* make_struct_decl(int lineno, string name, list* members) { + Declaration* d = new Declaration(); + d->tag = StructDeclaration; + d->u.struct_def = new struct StructDefinition(); + d->u.struct_def->lineno = lineno; + d->u.struct_def->name = new string(name); + d->u.struct_def->members = members; + return d; +} + +Declaration* make_choice_decl(int lineno, string name, + list >* alts) { + Declaration* d = new Declaration(); + d->tag = ChoiceDeclaration; + d->u.choice_def.lineno = lineno; + d->u.choice_def.name = new string(name); + d->u.choice_def.alternatives = alts; + return d; +} + +void print_params(VarTypes* ps) { + int i = 0; + for (auto iter = ps->begin(); iter != ps->end(); ++iter, ++i) { + if (i != 0) + cout << ", "; + print_exp(iter->second); + cout << ": "; + cout << iter->first; + } +} + +void print_var_decls(VarTypes* ps) { + int i = 0; + for (auto iter = ps->begin(); iter != ps->end(); ++iter, ++i) { + cout << "var "; + cout << iter->first; + cout << " : "; + print_exp(iter->second); + cout << "; "; + } +} + +void print_fun_def_depth(struct FunctionDefinition* f, int depth) { + cout << "fn " << f->name << " "; + print_exp(f->param_pattern); + cout << " -> "; + print_exp(f->return_type); + if (f->body) { + cout << " {" << endl; + print_stmt(f->body, depth); + cout << endl << "}" << endl; + } else { + cout << ";" << endl; + } +} + +void print_fun_def(struct FunctionDefinition* f) { + print_fun_def_depth(f, -1); +} + +void print_member(Member* m) { + switch (m->tag) { + case FieldMember: + cout << "var " << * m->u.field.name << " : "; + print_exp(m->u.field.type); + cout << ";" << endl; + break; + } +} + +void print_decl(Declaration* d) { + switch (d->tag) { + case FunctionDeclaration: + print_fun_def(d->u.fun_def); + break; + case StructDeclaration: + cout << "struct " << * d->u.struct_def->name << " {" << endl; + for (auto m = d->u.struct_def->members->begin(); + m != d->u.struct_def->members->end(); ++m) { + print_member(*m); + } + cout << "}" << endl; + break; + case ChoiceDeclaration: + cout << "choice " << * d->u.choice_def.name << " {" << endl; + for (auto a = d->u.choice_def.alternatives->begin(); + a != d->u.choice_def.alternatives->end(); ++a) { + cout << "alt " << a->first << " "; + print_exp(a->second); + cout << ";" << endl; + } + cout << "}" << endl; + break; + } +} + + +char *read_file(FILE* fp) +{ + char *fcontent = NULL; + int fsize = 0; + + if(fp) { + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + rewind(fp); + + fcontent = (char*) malloc(sizeof(char) * fsize); + fread(fcontent, 1, fsize, fp); + + fclose(fp); + } + return fcontent; +} diff --git a/executable-semantics/ast.h b/executable-semantics/ast.h new file mode 100644 index 0000000000000..fec2a33e69089 --- /dev/null +++ b/executable-semantics/ast.h @@ -0,0 +1,238 @@ +#ifndef AST_H +#define AST_H + +#include +#include +#include +#include +#include + +using std::string; +using std::list; +using std::vector; +using std::pair; + +/***** Utilities *****/ + +template +void print_list(list* ts, void(*printer)(T*), const char* sep) { + int i = 0; + for (auto iter = ts->begin(); iter != ts->end(); ++iter, ++i) { + if (i != 0) + printf("%s", sep); + printer(*iter); + } +} + +template +void print_vector(vector* ts, void(*printer)(T*), const char* sep) { + int i = 0; + for (auto iter = ts->begin(); iter != ts->end(); ++iter, ++i) { + if (i != 0) + printf("%s", sep); + printer(*iter); + } +} + +char *read_file(FILE* fp); + +extern char* input; + +/***** Forward Declarations *****/ + +struct LValue; +struct Expression; +struct Statement; +struct FunctionDefinition; + +typedef list< pair > VarTypes; + +/***** Expressions *****/ + +enum ExpressionKind { Variable, PatternVariable, Integer, Boolean, + PrimitiveOp, Call, Tuple, Index, GetField, + IntT, BoolT, TypeT, FunctionT, AutoT }; +enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; + +struct Expression { + int lineno; + ExpressionKind tag; + union { + struct { string* name; } variable; + struct { Expression* aggregate; string* field; } get_field; + struct { Expression* aggregate; Expression* offset; } index; + struct { string* name; Expression* type; } pattern_variable; + int integer; + bool boolean; + struct { vector >* fields; } tuple; + struct { Operator operator_; vector* arguments; } primitive_op; + struct { Expression* function; Expression* argument; } call; + struct { Expression* parameter; Expression* return_type; } function_type; + } u; +}; + +Expression* make_var(int lineno, string var); +Expression* make_var_pat(int lineno, string var, Expression* type); +Expression* make_int(int lineno, int i); +Expression* make_bool(int lineno, bool b); +Expression* make_op(int lineno, Operator op, vector* args); +Expression* make_unop(int lineno, enum Operator op, Expression* arg); +Expression* make_binop(int lineno, enum Operator op, + Expression* arg1, Expression* arg2); +Expression* make_call(int lineno, Expression* fun, Expression* arg); +Expression* make_get_field(int lineno, Expression* exp, string field); +Expression* make_tuple(int lineno, vector >* args); +Expression* make_index(int lineno, Expression* exp, Expression* i); + +Expression* make_type_type(int lineno); +Expression* make_int_type(int lineno); +Expression* make_bool_type(int lineno); +Expression* make_fun_type(int lineno, Expression* param, Expression* ret); +Expression* make_auto_type(int lineno); + +void print_exp(Expression*); + +/***** Expression or Field List *****/ +/* + This is used in the parsing of tuples and parenthesized expressions. + */ + +enum ExpOrFieldListKind { Exp, FieldList }; + +struct ExpOrFieldList { + ExpOrFieldListKind tag; + union { + Expression* exp; + list >* fields; + } u; +}; + +ExpOrFieldList* make_exp(Expression* exp); +ExpOrFieldList* make_field_list(list >* fields); +ExpOrFieldList* cons_field(ExpOrFieldList* e1, ExpOrFieldList* e2); + +/***** Statements *****/ + +enum StatementKind { ExpressionStatement, Assign, VariableDefinition, + If, Return, Sequence, Block, While, Break, Continue, + Match }; + +struct Statement { + int lineno; + StatementKind tag; + union { + Expression* exp; + struct { Expression* lhs; Expression* rhs; } assign; + struct { Expression* pat; Expression* init; } variable_definition; + struct { Expression* cond; Statement* thn; Statement* els; } if_stmt; + Expression* return_stmt; + struct { Statement* stmt; Statement* next; } sequence; + struct { Statement* stmt; } block; + struct { Expression* cond; Statement* body; } while_stmt; + struct { + Expression* exp; + list< pair >* clauses; + } match_stmt; + } u; +}; + +Statement* make_exp_stmt(int lineno, Expression* exp); +Statement* make_assign(int lineno, Expression* lhs, Expression* rhs); +Statement* make_var_def(int lineno, Expression* pat, Expression* init); +Statement* make_if(int lineno, Expression* cond, Statement* thn, + Statement* els); +Statement* make_return(int lineno, Expression* e); +Statement* make_seq(int lineno, Statement* s1, Statement* s2); +Statement* make_block(int lineno, Statement* s); +Statement* make_while(int lineno, Expression* cond, Statement* body); +Statement* make_break(int lineno); +Statement* make_continue(int lineno); +Statement* make_match(int lineno, Expression* exp, + list< pair >* clauses); + +void print_stmt(Statement*, int); + +/***** Function Definitions *****/ + +struct FunctionDefinition { + int lineno; + string name; + Expression* param_pattern; + Expression* return_type; + Statement* body; +}; + +/***** Struct Members *****/ + +enum MemberKind { FieldMember }; + +struct Member { + int lineno; + MemberKind tag; + union { + struct { string* name; Expression* type; } field; + } u; +}; + +Member* make_field(int lineno, string name, Expression* type); + +/***** Declarations *****/ + +struct StructDefinition { + int lineno; + string* name; + list* members; +}; + +enum DeclarationKind { FunctionDeclaration, StructDeclaration, + ChoiceDeclaration }; + +struct Declaration { + DeclarationKind tag; + union { + struct FunctionDefinition* fun_def; + struct StructDefinition* struct_def; + struct { + int lineno; + string* name; + list >* alternatives; + } choice_def; + } u; +}; + + +struct FunctionDefinition* +make_fun_def(int lineno, string name, Expression* ret_type, + Expression* param, Statement* body); +void print_fun_def(struct FunctionDefinition*); +void print_fun_def_depth(struct FunctionDefinition*, int); + +Declaration* make_fun_decl(struct FunctionDefinition* f); +Declaration* make_struct_decl(int lineno, string name, list* members); +Declaration* make_choice_decl(int lineno, string name, + list >* alts); + +void print_decl(Declaration*); + +void print_string(string* s); + +template +T find_field(string field, vector >* inits) { + for (auto i = inits->begin(); i != inits->end(); ++i) { + if (i->first == field) + return i->second; + } + throw std::domain_error(field); +} + +template +T find_alist(string field, list >* inits) { + for (auto i = inits->begin(); i != inits->end(); ++i) { + if (i->first == field) + return i->second; + } + throw std::domain_error(field); +} + + +#endif diff --git a/executable-semantics/cons_list.h b/executable-semantics/cons_list.h new file mode 100644 index 0000000000000..75ab84813de39 --- /dev/null +++ b/executable-semantics/cons_list.h @@ -0,0 +1,26 @@ +#ifndef CONS_LIST_H +#define CONS_LIST_H + +template +struct Cons { + T curr; + Cons* next; + Cons(T e, Cons* n) : curr(e), next(n) { } +}; + +template +Cons* cons(const T& x, Cons* ls) { + return new Cons(x, ls); +} + +template +unsigned int length(Cons* ls) { + if (ls) { + return 1 + length(ls->next); + } else { + return 0; + } +} + + +#endif diff --git a/executable-semantics/examples/block1.6c b/executable-semantics/examples/block1.6c new file mode 100644 index 0000000000000..1ef3155006898 --- /dev/null +++ b/executable-semantics/examples/block1.6c @@ -0,0 +1,7 @@ +fn main() -> Int { + var Int: x = 0; + { + var Int: x = 1; + } + return x; +} diff --git a/executable-semantics/examples/break1.6c b/executable-semantics/examples/break1.6c new file mode 100644 index 0000000000000..2323045c185a4 --- /dev/null +++ b/executable-semantics/examples/break1.6c @@ -0,0 +1,10 @@ +fn main() -> Int { + var Int: x = 2; + while (true) { + if (x == 0) + break; + else + x = x - 1; + } + return x; +} \ No newline at end of file diff --git a/executable-semantics/examples/choice1.6c b/executable-semantics/examples/choice1.6c new file mode 100644 index 0000000000000..ef10b189b1b0d --- /dev/null +++ b/executable-semantics/examples/choice1.6c @@ -0,0 +1,28 @@ +choice Ints { + None(); + One(Int); + Two(Int,Int); +} + +fn main() -> Int { + var auto: x = Ints.None(); + var auto: y = Ints.One(42); + var auto: n = 0; + match (y) { + case Ints.None() => + n = n + 2; + case Ints.One(auto: x) => + n = x + 1 - 42; + case Ints.Two(auto: a, auto: b) => + n = 2; + } + match (x) { + case Ints.One(auto: x) => + n = x + 2; + case Ints.None() => + n = n - 1; + case Ints.Two(auto: x, auto: y) => + n = 5; + } + return n; +} \ No newline at end of file diff --git a/executable-semantics/examples/continue1.6c b/executable-semantics/examples/continue1.6c new file mode 100644 index 0000000000000..2d454e099c6a0 --- /dev/null +++ b/executable-semantics/examples/continue1.6c @@ -0,0 +1,9 @@ +fn main() -> Int { + var auto: x = 2; + while (not (x == 0)) { + x = x - 1; + continue; + x = x + 1; + } + return x; +} \ No newline at end of file diff --git a/executable-semantics/examples/fun-recur.6c b/executable-semantics/examples/fun-recur.6c new file mode 100644 index 0000000000000..d4da3022557e5 --- /dev/null +++ b/executable-semantics/examples/fun-recur.6c @@ -0,0 +1,10 @@ +fn f(Int: x) -> Int { + if (x == 0) + return x; + else + return f(x - 1); +} + +fn main() -> Int { + return f(2); +} diff --git a/executable-semantics/examples/fun1.6c b/executable-semantics/examples/fun1.6c new file mode 100644 index 0000000000000..ee7ec04901305 --- /dev/null +++ b/executable-semantics/examples/fun1.6c @@ -0,0 +1,7 @@ +fn f(Int: x) -> Int { + return x - 1; +} + +fn main() -> Int { + return f(1); +} \ No newline at end of file diff --git a/executable-semantics/examples/fun2.6c b/executable-semantics/examples/fun2.6c new file mode 100644 index 0000000000000..94e5d5f5f1ed3 --- /dev/null +++ b/executable-semantics/examples/fun2.6c @@ -0,0 +1,14 @@ +// This tests the call-by-value aspect of parameter passing. +// This makes sure that when the value in `x` dies, +// it does not cause the value in `a` to also die. + +fn f(Int: x) -> Int { + return 0; +} + +fn main() -> Int { + var Int: a = 0; var Int: b = 1; + f(a); + b = a; + return b; +} \ No newline at end of file diff --git a/executable-semantics/examples/fun3.6c b/executable-semantics/examples/fun3.6c new file mode 100644 index 0000000000000..b977e2ef2a8ee --- /dev/null +++ b/executable-semantics/examples/fun3.6c @@ -0,0 +1,8 @@ +// Test multiple arguments +fn f(Int: x, Int: y) -> Int { + return x + y; +} + +fn main() -> Int { + return f(2,3) - 5; +} \ No newline at end of file diff --git a/executable-semantics/examples/fun4.6c b/executable-semantics/examples/fun4.6c new file mode 100644 index 0000000000000..dadf3b92a7838 --- /dev/null +++ b/executable-semantics/examples/fun4.6c @@ -0,0 +1,7 @@ +// Test empty parameters and return type +fn f() { } + +fn main() -> Int { + f(); + return 0; +} \ No newline at end of file diff --git a/executable-semantics/examples/fun5.6c b/executable-semantics/examples/fun5.6c new file mode 100644 index 0000000000000..189d36f898578 --- /dev/null +++ b/executable-semantics/examples/fun5.6c @@ -0,0 +1,5 @@ +fn add(Int: x, Int: y) => x + y; + +fn main() -> Int { + return add(1, 2) - 3; +} \ No newline at end of file diff --git a/executable-semantics/examples/funptr1.6c b/executable-semantics/examples/funptr1.6c new file mode 100644 index 0000000000000..3e02d6462150a --- /dev/null +++ b/executable-semantics/examples/funptr1.6c @@ -0,0 +1,8 @@ +fn add1(Int: x) -> Int { + return x + 1; +} + +fn main() -> Int { + var fnty(Int)->Int: f = add1; + return f(-1); +} diff --git a/executable-semantics/examples/match-int-default.6c b/executable-semantics/examples/match-int-default.6c new file mode 100644 index 0000000000000..cd5e36f3f6809 --- /dev/null +++ b/executable-semantics/examples/match-int-default.6c @@ -0,0 +1,11 @@ +fn main() -> Int { + var auto: t = 5; + match (t) { + case 3 => + return -1; + case 4 => + return -1; + default => + return 0; + } +} \ No newline at end of file diff --git a/executable-semantics/examples/match-int.6c b/executable-semantics/examples/match-int.6c new file mode 100644 index 0000000000000..9dd5d57b2f46d --- /dev/null +++ b/executable-semantics/examples/match-int.6c @@ -0,0 +1,9 @@ +fn main() -> Int { + var auto: t = 5; + match (t) { + case 5 => + return 0; + default => + return 1; + } +} \ No newline at end of file diff --git a/executable-semantics/examples/match-type.6c b/executable-semantics/examples/match-type.6c new file mode 100644 index 0000000000000..4ed15d7786d7e --- /dev/null +++ b/executable-semantics/examples/match-type.6c @@ -0,0 +1,17 @@ +fn main() -> Int { + var auto: t = fnty (Int,Int); + var Int: x = 0; +// match (t) { +// case fnty(Int: x, Int: y): z => +// x = x - 1; +// } + match (t) { + case fnty(Int,Int): z => + x = x + 1; + } + match (t) { + case fnty(Type: a,Type: b) => + x = x - 1; + } + return x; +} \ No newline at end of file diff --git a/executable-semantics/examples/next.6c b/executable-semantics/examples/next.6c new file mode 100644 index 0000000000000..796f696289c1b --- /dev/null +++ b/executable-semantics/examples/next.6c @@ -0,0 +1,5 @@ +fn main () -> Int +{ + var Int: x = 0; + return x; +} diff --git a/executable-semantics/examples/pattern-init.6c b/executable-semantics/examples/pattern-init.6c new file mode 100644 index 0000000000000..d5e6f8d96f392 --- /dev/null +++ b/executable-semantics/examples/pattern-init.6c @@ -0,0 +1,4 @@ +fn main() -> Int { + var (auto: x, auto: y) = (2, 3); + return y - x - 1; +} \ No newline at end of file diff --git a/executable-semantics/examples/record1.6c b/executable-semantics/examples/record1.6c new file mode 100644 index 0000000000000..e57abe5f31574 --- /dev/null +++ b/executable-semantics/examples/record1.6c @@ -0,0 +1,5 @@ +fn main() -> Int { + var (.x = Int, .y = Int): t2 = (.y = 5, .x = 2); + t2.y = 3; + return t2.y - t2.x - 1; // 3 - 2 - 1 +} \ No newline at end of file diff --git a/executable-semantics/examples/struct1.6c b/executable-semantics/examples/struct1.6c new file mode 100644 index 0000000000000..668cf951a2aff --- /dev/null +++ b/executable-semantics/examples/struct1.6c @@ -0,0 +1,9 @@ +struct Point { + var Int: x; + var Int: y; +} + +fn main() -> Int { + var auto: p = Point(.x = 1, .y = 2); + return p.y - p.x - 1; +} \ No newline at end of file diff --git a/executable-semantics/examples/struct2.6c b/executable-semantics/examples/struct2.6c new file mode 100644 index 0000000000000..0f284df2d0178 --- /dev/null +++ b/executable-semantics/examples/struct2.6c @@ -0,0 +1,11 @@ +struct Point { + var Int: x; + var Int: y; +} + +fn main() -> Int { + var auto: p1 = Point(.x = 1, .y = 2); + var auto: p2 = p1; + p2.x = 3; + return p1.x - 1; +} \ No newline at end of file diff --git a/executable-semantics/examples/struct3.6c b/executable-semantics/examples/struct3.6c new file mode 100644 index 0000000000000..c73c93ce207c8 --- /dev/null +++ b/executable-semantics/examples/struct3.6c @@ -0,0 +1,8 @@ +struct Point { + var Int: x; + var Int: y; +} + +fn main() -> Int { + return Point(.x = 1, .y = 2).x - 1; +} \ No newline at end of file diff --git a/executable-semantics/examples/tuple-assign.6c b/executable-semantics/examples/tuple-assign.6c new file mode 100644 index 0000000000000..483f7c935d08c --- /dev/null +++ b/executable-semantics/examples/tuple-assign.6c @@ -0,0 +1,6 @@ +fn main() -> Int { + var auto: x = 0; + var auto: y = 1; + (x, y) = (5, -5); + return x + y; +} \ No newline at end of file diff --git a/executable-semantics/examples/tuple-match.6c b/executable-semantics/examples/tuple-match.6c new file mode 100644 index 0000000000000..2c417ab3682e2 --- /dev/null +++ b/executable-semantics/examples/tuple-match.6c @@ -0,0 +1,7 @@ +fn main() -> Int { + var auto: t = (5, 2); + match (t) { + case (auto: a, auto: b) => + return a + b - 7; + } +} \ No newline at end of file diff --git a/executable-semantics/examples/tuple1.6c b/executable-semantics/examples/tuple1.6c new file mode 100644 index 0000000000000..bf97070d4aa9b --- /dev/null +++ b/executable-semantics/examples/tuple1.6c @@ -0,0 +1,6 @@ +fn main() -> Int { + var Int: x = (1); + var (Int,Int): t2 = (5, 2); + t2[0] = 3; + return t2[0] - t2[1] - x; +} \ No newline at end of file diff --git a/executable-semantics/examples/tuple2.6c b/executable-semantics/examples/tuple2.6c new file mode 100644 index 0000000000000..326c4f8f1632b --- /dev/null +++ b/executable-semantics/examples/tuple2.6c @@ -0,0 +1,4 @@ +fn main() -> Int { + var (Int,): t = (5,); + return t[0] - 5; +} \ No newline at end of file diff --git a/executable-semantics/examples/undef1.6c b/executable-semantics/examples/undef1.6c new file mode 100644 index 0000000000000..dde42e3451ec6 --- /dev/null +++ b/executable-semantics/examples/undef1.6c @@ -0,0 +1,14 @@ +// The behavior of this program is undefined because it tries to +// access a local variable after its lifetime is over. + +fn f() -> Int* { + var int: x; + x = 0; + return &x; +} + +fn main() -> int { + var Ptr(int): p; + p = f(); + return *p; +} diff --git a/executable-semantics/examples/undef2.6c b/executable-semantics/examples/undef2.6c new file mode 100644 index 0000000000000..567d8bb048aec --- /dev/null +++ b/executable-semantics/examples/undef2.6c @@ -0,0 +1,9 @@ +// Use-after-free. + +fun main() -> Int { + var Ptr(int): p; + p = malloc(Int); + *p = 0; + free(p); + return *p; +} diff --git a/executable-semantics/examples/while1.6c b/executable-semantics/examples/while1.6c new file mode 100644 index 0000000000000..a7f526a0c9103 --- /dev/null +++ b/executable-semantics/examples/while1.6c @@ -0,0 +1,6 @@ +fn main() -> Int { + var auto: x = 2; + while (not (x == 0)) + x = x - 1; + return x; +} diff --git a/executable-semantics/examples/zero.6c b/executable-semantics/examples/zero.6c new file mode 100644 index 0000000000000..2c5c5f1891403 --- /dev/null +++ b/executable-semantics/examples/zero.6c @@ -0,0 +1,3 @@ +fn main() -> Int { + return 0; +} \ No newline at end of file diff --git a/executable-semantics/interp.cc b/executable-semantics/interp.cc new file mode 100644 index 0000000000000..1872996cfb3e1 --- /dev/null +++ b/executable-semantics/interp.cc @@ -0,0 +1,1704 @@ +#include +#include +#include +#include +#include "interp.h" +#include "typecheck.h" + +using std::vector; +using std::map; +using std::cout; +using std::cerr; +using std::endl; + +State* state; + +Env* pattern_match(Value* pat, Value* val, Env*, list&, int); +void handle_value(); + +/***** Value Operations *****/ + +int to_integer(Value* v) { + switch (v->tag) { + case IntV: + return v->u.integer; + default: + cerr << "expected an integer, not "; + print_value(v, cerr); + exit(-1); + } +} + +void check_alive(Value* v, int lineno) { + if (! v->alive) { + cerr << lineno << ": undefined behavior: access to dead value "; + print_value(v, cerr); + cerr << endl; + exit(-1); + } +} + +Value* make_int_val(int i) { + Value* v = new Value(); + v->alive = true; + v->tag = IntV; + v->u.integer = i; + return v; +} + +Value* make_bool_val(bool b) { + Value* v = new Value(); + v->alive = true; + v->tag = BoolV; + v->u.boolean = b; + return v; +} + +Value* make_fun_val(string name, Value* param, Statement* body) { + Value* v = new Value(); + v->alive = true; + v->tag = FunV; + v->u.fun.name = new string(name); + v->u.fun.param = param; + v->u.fun.body = body; + return v; +} + +Value* make_ptr_val(address addr) { + Value* v = new Value(); + v->alive = true; + v->tag = PtrV; + v->u.ptr = addr; + return v; +} + +Value* make_struct_val(Value* type, Value* inits) { + Value* v = new Value(); + v->alive = true; + v->tag = StructV; + v->u.struct_val.type = type; + v->u.struct_val.inits = inits; + return v; +} + +Value* make_tuple_val(vector >* elts) { + Value* v = new Value(); + v->alive = true; + v->tag = TupleV; + v->u.tuple.elts = elts; + return v; +} + +Value* make_alt_val(string alt_name, string choice_name, Value* arg) { + Value* v = new Value(); + v->alive = true; + v->tag = AltV; + v->u.alt.alt_name = new string(alt_name); + v->u.alt.choice_name = new string(choice_name); + v->u.alt.arg = arg; + return v; +} + +Value* make_alt_cons(string alt_name, string choice_name) { + Value* v = new Value(); + v->alive = true; + v->tag = AltConsV; + v->u.alt.alt_name = new string(alt_name); + v->u.alt.choice_name = new string(choice_name); + return v; +} + +Value* make_var_pat_val(string name, Value* type) { + Value* v = new Value(); + v->alive = true; + v->tag = VarPatV; + v->u.var_pat.name = new string(name); + v->u.var_pat.type = type; + return v; +} + +Value* make_var_type_val(string name) { + Value* v = new Value(); + v->alive = true; + v->tag = VarTV; + v->u.var_type = new string(name); + return v; +} + +Value* make_int_type_val() { + Value* v = new Value(); + v->alive = true; + v->tag = IntTV; + return v; +} + +Value* make_bool_type_val() { + Value* v = new Value(); + v->alive = true; + v->tag = BoolTV; + return v; +} + +Value* make_type_type_val() { + Value* v = new Value(); + v->alive = true; + v->tag = TypeTV; + return v; +} + +Value* make_auto_type_val() { + Value* v = new Value(); + v->alive = true; + v->tag = AutoTV; + return v; +} + +Value* make_fun_type_val(Value* param, Value* ret) { + Value* v = new Value(); + v->alive = true; + v->tag = FunctionTV; + v->u.fun_type.param = param; + v->u.fun_type.ret = ret; + return v; +} + +Value* make_ptr_type_val(Value* type) { + Value* v = new Value(); + v->alive = true; + v->tag = PointerTV; + v->u.ptr_type.type = type; + return v; +} + +Value* make_struct_type_val(string name, VarValues* fields, VarValues* methods){ + Value* v = new Value(); + v->alive = true; + v->tag = StructTV; + v->u.struct_type.name = new string(name); + v->u.struct_type.fields = fields; + v->u.struct_type.methods = methods; + return v; +} + +Value* make_tuple_type_val(VarValues* fields) { + Value* v = new Value(); + v->alive = true; + v->tag = TupleTV; + v->u.tuple_type.fields = fields; + return v; +} + +Value* make_void_type_val() { + Value* v = new Value(); + v->alive = true; + v->tag = TupleTV; + v->u.tuple_type.fields = new VarValues(); + return v; +} + + +Value* make_choice_type_val(string* name, list >* alts){ + Value* v = new Value(); + v->alive = true; + v->tag = ChoiceTV; + v->u.choice_type.name = name; + v->u.choice_type.alternatives = alts; + return v; +} + +/**** Auxiliary Functions ****/ + + +address allocate_value(Value* v) { + // Putting the following two side effects together in this function + // ensures that we don't do anything else in between, which is really bad! + // Consider whether to include a copy of the input v in this function + // or to leave it up to the caller. + address a = state->heap.size(); + state->heap.push_back(v); + return a; +} + +Value* copy_val(Value* val, int lineno) { + check_alive(val, lineno); + switch (val->tag) { + case TupleV: { + auto elts = new vector >(); + for (auto i = val->u.tuple.elts->begin(); + i != val->u.tuple.elts->end(); ++i) { + Value* elt = copy_val(state->heap[i->second], lineno); + elts->push_back(make_pair(i->first, allocate_value(elt))); + } + return make_tuple_val(elts); + } + case AltV: { + Value* arg = copy_val(val->u.alt.arg, lineno); + return make_alt_val(*val->u.alt.alt_name, *val->u.alt.choice_name, arg); + } + case StructV: { + Value* inits = copy_val(val->u.struct_val.inits, lineno); + return make_struct_val(val->u.struct_val.type, inits); + } + case IntV: + return make_int_val(val->u.integer); + case BoolV: + return make_bool_val(val->u.boolean); + case FunV: + return make_fun_val(*val->u.fun.name, val->u.fun.param, val->u.fun.body); + case PtrV: + return make_ptr_val(val->u.ptr); + case FunctionTV: + return make_fun_type_val(copy_val(val->u.fun_type.param, lineno), + copy_val(val->u.fun_type.ret, lineno)); + + case PointerTV: + return make_ptr_type_val(copy_val(val->u.ptr_type.type, lineno)); + case IntTV: + return make_int_type_val(); + case BoolTV: + return make_bool_type_val(); + case TypeTV: + return make_type_type_val(); + case VarTV: + return make_var_type_val(* val->u.var_type); + case AutoTV: + return make_auto_type_val(); + case TupleTV: { + auto new_fields = new VarValues(); + for (auto i = val->u.tuple_type.fields->begin(); + i != val->u.tuple_type.fields->end(); ++i) { + auto v = copy_val(i->second, lineno); + new_fields->push_back(make_pair(i->first, v)); + } + return make_tuple_type_val(new_fields); + } + case StructTV: case ChoiceTV: + case VarPatV: case AltConsV: + return val; // no need to copy these because they are immutable? + // No, they need to be copied so they don't get killed. -Jeremy + } +} + +void kill_value(Value* val) { + val->alive = false; + switch (val->tag) { + case AltV: + kill_value(val->u.alt.arg); + break; + case StructV: + kill_value(val->u.struct_val.inits); + break; + case TupleV: + for (auto i = val->u.tuple.elts->begin(); + i != val->u.tuple.elts->end(); ++i) { + if (state->heap[i->second]->alive) + kill_value(state->heap[i->second]); + else { + cerr << "runtime error, killing an already dead value" << endl; + exit(-1); + } + } + break; + default: + break; + } +} + +void print_env(Env* env, std::ostream& out) { + if (env) { + cout << env->key << ": "; + print_value(state->heap[env->value], out); + cout << ", "; + print_env(env->next, out); + } +} + +void print_value(Value* val, std::ostream& out) { + if (! val->alive) { + out << "!!"; + } + switch (val->tag) { + case AltConsV: { + out << * val->u.alt_cons.choice_name << "." << * val->u.alt_cons.alt_name; + break; + } + case VarPatV: { + print_value(val->u.var_pat.type, out); + out << ": " << *val->u.var_pat.name; + break; + } + case AltV: { + out << "alt " + << * val->u.alt.choice_name + << "." + << * val->u.alt.alt_name + << " "; + print_value(val->u.alt.arg, out); + break; + } + case StructV: { + out << * val->u.struct_val.type->u.struct_type.name; + print_value(val->u.struct_val.inits, out); + break; + } + case TupleV: { + out << "("; + int i = 0; + for (auto elt = val->u.tuple.elts->begin(); + elt != val->u.tuple.elts->end(); ++elt, ++i) { + if (i != 0) + out << ", "; + out << elt->first << " = "; + print_value(state->heap[elt->second], out); + out << "@" << elt->second; + } + out << ")"; + break; + } + case IntV: + out << val->u.integer; + break; + case BoolV: + out << std::boolalpha; + out << val->u.boolean; + break; + case FunV: + out << "fun<" << * val->u.fun.name << ">"; + break; + case PtrV: + out << "ptr<" << val->u.ptr << ">"; + break; + case BoolTV: + out << "Bool"; + break; + case IntTV: + out << "Int"; + break; + case TypeTV: + out << "Type"; + break; + case AutoTV: + out << "auto"; + break; + case PointerTV: + out << "Ptr("; + print_value(val->u.ptr_type.type, out); + out << ")"; + break; + case FunctionTV: + out << "fn "; + print_value(val->u.fun_type.param, out); + out << " -> "; + print_value(val->u.fun_type.ret, out); + break; + case VarTV: + out << * val->u.var_type; + break; + case TupleTV: { + out << "Tuple("; + int i = 0; + for (auto elt = val->u.tuple_type.fields->begin(); + elt != val->u.tuple_type.fields->end(); ++elt, ++i) { + if (i != 0) + out << ", "; + out << elt->first << " = "; + print_value(elt->second, out); + } + out << ")"; + break; + } + case StructTV: + out << "struct " << * val->u.struct_type.name; + break; + case ChoiceTV: + out << "choice " << * val->u.choice_type.name; + break; + } +} + +/***** Action Operations *****/ + +void print_act(Action* act, std::ostream& out) { + switch (act->tag) { + case DeleteTmpAction: + cout << "delete_tmp(" << act->u.delete_ << ")"; + break; + case ExpToLValAction: + out << "exp=>lval"; + break; + case LValAction: + case ExpressionAction: + print_exp(act->u.exp); + break; + case StatementAction: + print_stmt(act->u.stmt, 1); + break; + case ValAction: + print_value(act->u.val, out); + break; + } + out << "<" << act->pos << ">"; + if (act->results.size() > 0) { + out << "("; + for (auto iter = act->results.begin(); iter != act->results.end(); ++iter) { + if (*iter) + print_value(*iter, out); + out << ","; + } + out << ")"; + } +} + +void print_act_list(Cons* ls, std::ostream& out) { + if (ls) { + print_act(ls->curr, out); + if (ls->next) { + out << " :: "; + print_act_list(ls->next, out); + } + } +} + +Action* make_exp_act(Expression* e) { + Action* act = new Action(); + act->tag = ExpressionAction; + act->u.exp = e; + act->pos = -1; + return act; +} + +Action* make_lval_act(Expression* e) { + Action* act = new Action(); + act->tag = LValAction; + act->u.exp = e; + act->pos = -1; + return act; +} + +Action* make_stmt_act(Statement* s) { + Action* act = new Action(); + act->tag = StatementAction; + act->u.stmt = s; + act->pos = -1; + return act; +} + +Action* make_val_act(Value* v) { + Action* act = new Action(); + act->tag = ValAction; + act->u.val = v; + act->pos = -1; + return act; +} + +Action* make_exp_to_lval_act() { + Action* act = new Action(); + act->tag = ExpToLValAction; + act->pos = -1; + return act; +} + +Action* make_delete_act(address a) { + Action* act = new Action(); + act->tag = DeleteTmpAction; + act->pos = -1; + act->u.delete_ = a; + return act; +} + +/***** Frame and State Operations *****/ + +void print_frame(Frame* frame, std::ostream& out) { + out << frame->name; + out << "{"; + print_act_list(frame->todo, out); + out << "}"; +} + +void print_stack(Cons* ls, std::ostream& out) { + if (ls) { + print_frame(ls->curr, out); + if (ls->next) { + out << " :: "; + print_stack(ls->next, out); + } + } +} + +void print_heap(vector& heap, std::ostream& out) { + for (auto iter = heap.begin(); iter != heap.end(); ++iter) { + if (*iter) { + print_value(*iter, out); + } else { + out << "_"; + } + out << ", "; + } +} + +Env* current_env(State* state) { + Frame* frame = state->stack->curr; + return frame->scopes->curr->env; +} + +void print_state(std::ostream& out) { + out << "{" << endl; + out << "stack: "; + print_stack(state->stack, out); + out << endl << "heap: "; + print_heap(state->heap, out); + out << endl << "env: "; + print_env(current_env(state), out); + out << endl << "}" << endl; +} + +/***** Auxilliary Functions *****/ + + +int val_to_int(Value* v, int lineno) { + check_alive(v, lineno); + switch (v->tag) { + case IntV: + return v->u.integer; + default: + cerr << lineno << ": runtime error: expected an integer" << endl; + exit(-1); + } +} + +int val_to_bool(Value* v, int lineno) { + check_alive(v, lineno); + switch (v->tag) { + case BoolV: + return v->u.boolean; + default: + cerr << "runtime type error: expected a Boolean" << endl; + exit(-1); + } +} + +address val_to_ptr(Value* v, int lineno) { + check_alive(v, lineno); + switch (v->tag) { + case PtrV: + return v->u.ptr; + default: + cerr << "runtime type error: expected a pointer, not "; + print_value(v, cerr); + cerr << endl; + exit(-1); + } +} + +bool fields_value_equal(VarValues* ts1, VarValues* ts2, int lineno) { + if (ts1->size() == ts2->size()) { + for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1) { + try { + auto t2 = find_alist(iter1->first, ts2); + if (! value_equal(iter1->second, t2, lineno)) + return false; + } catch (std::domain_error de) { + return false; + } + } + return true; + } else { + return false; + } +} + +bool value_equal(Value* v1, Value* v2, int lineno) { + check_alive(v1, lineno); + check_alive(v2, lineno); + return (v1->tag == IntV && v2->tag == IntV && v1->u.integer == v2->u.integer) + || (v1->tag == BoolV && v2->tag == BoolV && v1->u.boolean == v2->u.boolean) + || (v1->tag == PtrV && v2->tag == PtrV && v1->u.ptr == v2->u.ptr) + || (v1->tag == FunV && v2->tag == FunV && v1->u.fun.body == v2->u.fun.body) + || (v1->tag == TupleV && v2->tag == TupleV + && fields_value_equal(v1->u.tuple_type.fields, + v2->u.tuple_type.fields, lineno)) + // TODO: struct and alternative values + || type_equal(v1, v2); +} + +Value* eval_prim(Operator op, const vector& args, int lineno) { + switch (op) { + case Neg: + return make_int_val(- val_to_int(args[0], lineno)); + case Add: + return make_int_val(val_to_int(args[0], lineno) + + val_to_int(args[1], lineno)); + case Sub: + return make_int_val(val_to_int(args[0], lineno) + - val_to_int(args[1], lineno)); + case Not: + return make_bool_val(! val_to_bool(args[0], lineno)); + case And: + return make_bool_val(val_to_bool(args[0], lineno) + && val_to_bool(args[1], lineno)); + case Or: + return make_bool_val(val_to_bool(args[0], lineno) + || val_to_bool(args[1], lineno)); + case Eq: + return make_bool_val(value_equal(args[0], args[1], lineno)); + } +} + +Env* globals; + +void init_globals(list* fs) { + globals = 0; + for (auto iter = fs->begin(); iter != fs->end(); ++iter) { + switch ((*iter)->tag) { + case ChoiceDeclaration: { + auto d = *iter; + auto alts = new VarValues(); + for (auto i = d->u.choice_def.alternatives->begin(); + i != d->u.choice_def.alternatives->end(); ++i) { + auto t = to_type(d->u.choice_def.lineno, interp_exp(0, i->second)); + alts->push_back(make_pair(i->first, t)); + } + auto ct = make_choice_type_val(d->u.choice_def.name, alts); + auto a = allocate_value(ct); + globals = new Env(* d->u.choice_def.name, a, globals); + break; + } + case StructDeclaration: { + auto d = *iter; + auto fields = new VarValues(); + auto methods = new VarValues(); + for (auto i = d->u.struct_def->members->begin(); + i != d->u.struct_def->members->end(); ++i) { + switch ((*i)->tag) { + case FieldMember: { + auto t = to_type(d->u.struct_def->lineno, + interp_exp(0, (*i)->u.field.type)); + fields->push_back(make_pair(* (*i)->u.field.name, t)); + break; + } + } + } + auto st = make_struct_type_val(*d->u.struct_def->name, fields, methods); + auto a = allocate_value(st); + globals = new Env(* d->u.struct_def->name, a, globals); + break; + } + case FunctionDeclaration: { + struct FunctionDefinition* fun = (*iter)->u.fun_def; + Env* env = 0; + VarValues* implicit_params = 0; + auto pt = interp_exp(env, fun->param_pattern); + auto f = make_fun_val(fun->name, pt, fun->body); + address a = allocate_value(f); + globals = new Env(fun->name, a, globals); + break; + } + } + } +} + +// { S, H} -> { { C, E, F} :: S, H} +// where C is the body of the function, +// E is the environment (functions + parameters + locals) +// F is the function +void call_function(int lineno, vector operas, State* state) { + check_alive(operas[0], lineno); + switch (operas[0]->tag) { + case FunV: { + Env* env = globals; + // Bind arguments to parameters + list params; + env = pattern_match(operas[0]->u.fun.param, operas[1], env, params, lineno); + if (!env) { + cerr << "internal error in call_function, pattern match failed" << endl; + exit(-1); + } + // Create the new frame and push it on the stack + Scope* scope = new Scope(env, params); + Frame* frame = new Frame(* operas[0]->u.fun.name, + cons(scope, (Cons*)0), + cons(make_stmt_act(operas[0]->u.fun.body), + (Cons*)0)); + state->stack = cons(frame, state->stack); + break; + } + case StructTV: { + Value* arg = copy_val(operas[1], lineno); + Value* sv = make_struct_val(operas[0], arg); + Frame* frame = state->stack->curr; + frame->todo = cons(make_val_act(sv), frame->todo); + break; + } + case AltConsV: { + Value* arg = copy_val(operas[1], lineno); + Value* av = make_alt_val(* operas[0]->u.alt_cons.alt_name, + * operas[0]->u.alt_cons.choice_name, + arg); + Frame* frame = state->stack->curr; + frame->todo = cons(make_val_act(av), frame->todo); + break; + } + default: + cerr << lineno << ": in call, expected a function, not "; + print_value(operas[0], cerr); + cerr << endl; + exit(-1); + } +} + +void kill_scope(int lineno, Scope* scope) { + for (auto l = scope->locals.begin(); l != scope->locals.end(); ++l) { + address a = lookup(lineno, scope->env, *l, print_error_string); + kill_value(state->heap[a]); + } +} + +void kill_locals(int lineno, Frame* frame) { + Cons* scopes = frame->scopes; + for (Scope* scope = scopes->curr; scopes; scopes = scopes->next) { + kill_scope(lineno, scope); + } +} + +void create_tuple(Frame* frame, Action* act, Expression* exp) { + // { { (v1,...,vn) :: C, E, F} :: S, H} + // -> { { `(v1,...,vn) :: C, E, F} :: S, H} + auto elts = new vector>(); + auto f = act->u.exp->u.tuple.fields->begin(); + for (auto i = act->results.begin(); i != act->results.end(); ++i, ++f) { + address a = allocate_value(*i); // copy? + elts->push_back(make_pair(f->first, a)); + } + Value* tv = make_tuple_val(elts); + frame->todo = cons(make_val_act(tv), frame->todo->next); +} + +Value* to_value(Expression* value) { + switch (value->tag) { + case Integer: + return make_int_val(value->u.integer); + case Boolean: + return make_bool_val(value->u.boolean); + case IntT: + return make_int_type_val(); + case BoolT: + return make_bool_type_val(); + case TypeT: + return make_type_type_val(); + case FunctionT: + // instead add to patterns? + default: + cerr << "internal error in to_value, didn't expect "; + print_exp(value); + cerr << endl; + exit(-1); + } +} + + +// +// Returns 0 if the value doesn't match the pattern +// +Env* pattern_match(Value* p, Value* v, Env* env, + list& vars, int lineno) { + cout << "pattern_match("; + print_value(p, cout); + cout << ", "; + print_value(v, cout); + cout << ")" << endl; + switch (p->tag) { + case VarPatV: { + address a = allocate_value(copy_val(v, lineno)); + vars.push_back(*p->u.var_pat.name); + return new Env(*p->u.var_pat.name, a, env); + } + case TupleV: + switch (v->tag) { + case TupleV: { + if (p->u.tuple.elts->size() != v->u.tuple.elts->size()) { + cerr << "runtime error: arity mismatch in tuple pattern match" << endl; + exit(-1); + } + for (auto i = p->u.tuple.elts->begin(); i != p->u.tuple.elts->end(); + ++i) { + address a = find_field(i->first, v->u.tuple.elts); + env = pattern_match(state->heap[i->second], state->heap[a], + env, vars, lineno); + } + return env; + } + default: + cerr << "internal error, expected a tuple value in pattern, not "; + print_value(v, cerr); + cerr << endl; + exit(-1); + } + case AltV: + switch (v->tag) { + case AltV: { + if (*p->u.alt.choice_name != *v->u.alt.choice_name + || *p->u.alt.alt_name != *v->u.alt.alt_name) + return 0; + env = pattern_match(p->u.alt.arg, v->u.alt.arg, env, vars, lineno); + return env; + } + default: + cerr << "internal error, expected a choice alternative in pattern, not "; + print_value(v, cerr); + cerr << endl; + exit(-1); + } + case FunctionTV: + switch (v->tag) { + case FunctionTV: + env = pattern_match(p->u.fun_type.param, v->u.fun_type.param, env, vars, + lineno); + env = pattern_match(p->u.fun_type.ret, v->u.fun_type.ret, env, vars, + lineno); + return env; + default: + return 0; + } + default: + if (value_equal(p, v, lineno)) + return env; + else + return 0; + } +} + +void pattern_assignment(Value* pat, Value* val, int lineno) { + switch (pat->tag) { + case PtrV: + state->heap[val_to_ptr(pat, lineno)] = val; + break; + case TupleV: { + switch (val->tag) { + case TupleV: { + if (pat->u.tuple.elts->size() != val->u.tuple.elts->size()) { + cerr << "runtime error: arity mismatch in tuple pattern match" << endl; + exit(-1); + } + for (auto i = pat->u.tuple.elts->begin(); i != pat->u.tuple.elts->end(); + ++i) { + address a = find_field(i->first, val->u.tuple.elts); + pattern_assignment(state->heap[i->second], state->heap[a], lineno); + } + break; + } + default: + cerr << "internal error, expected a tuple value on right-hand-side, not "; + print_value(val, cerr); + cerr << endl; + exit(-1); + } + break; + } + case AltV: { + switch (val->tag) { + case AltV: { + if (*pat->u.alt.choice_name != *val->u.alt.choice_name + || *pat->u.alt.alt_name != *val->u.alt.alt_name) { + cerr << "internal error in pattern assignment" << endl; + exit(-1); + } + pattern_assignment(pat->u.alt.arg, val->u.alt.arg, lineno); + break; + } + default: + cerr << "internal error, expected an alternative in left-hand-side, not "; + print_value(val, cerr); + cerr << endl; + exit(-1); + } + break; + } + default: + if (! value_equal(pat, val, lineno)) { + cerr << "internal error in pattern assignment" << endl; + exit(-1); + } + } +} + + +/***** state transitions for lvalues *****/ + +void step_lvalue() { + Frame* frame = state->stack->curr; + Action* act = frame->todo->curr; + Expression* exp = act->u.exp; + cout << "--- step lvalue "; print_exp(exp); cout << " --->" << endl; + switch (exp->tag) { + case Variable: { + // { {x :: C, E, F} :: S, H} + // -> { {E(x) :: C, E, F} :: S, H} + address a = lookup(exp->lineno, current_env(state), *(exp->u.variable.name), + print_error_string); + Value* v = make_ptr_val(a); + check_alive(v, exp->lineno); + frame->todo = cons(make_val_act(v), frame->todo->next); + break; + } + case GetField: { + // { {e.f :: C, E, F} :: S, H} + // -> { e :: [].f :: C, E, F} :: S, H} + frame->todo = cons(make_lval_act(exp->u.get_field.aggregate), frame->todo); + act->pos++; + break; + } + case Index: { + // { {e[i] :: C, E, F} :: S, H} + // -> { e :: [][i] :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(exp->u.index.aggregate), frame->todo); + act->pos++; + break; + } + case Tuple: { + // { {(f1=e1,...) :: C, E, F} :: S, H} + // -> { {e1 :: (f1=[],...) :: C, E, F} :: S, H} + Expression* e1 = (*exp->u.tuple.fields)[0].second; + frame->todo = cons(make_lval_act(e1), frame->todo); + act->pos++; + break; + } + case Integer: case Boolean: case Call: case PrimitiveOp: + case IntT: case BoolT: case TypeT: case FunctionT: case AutoT: + case PatternVariable: { + frame->todo = cons(make_exp_act(exp), + cons(make_exp_to_lval_act(), + frame->todo->next)); + } + } +} + +/***** state transitions for expressions *****/ + +void step_exp() { + Frame* frame = state->stack->curr; + Action* act = frame->todo->curr; + Expression* exp = act->u.exp; + cout << "--- step exp "; print_exp(exp); cout << " --->" << endl; + switch (exp->tag) { + case PatternVariable: { + frame->todo = cons(make_exp_act(exp->u.pattern_variable.type), frame->todo); + act->pos++; + break; + } + case Index: { + // { { e[i] :: C, E, F} :: S, H} + // -> { { e :: [][i] :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(exp->u.index.aggregate), frame->todo); + act->pos++; + break; + } + case Tuple: { + if (exp->u.tuple.fields->size() > 0) { + // { {(f1=e1,...) :: C, E, F} :: S, H} + // -> { {e1 :: (f1=[],...) :: C, E, F} :: S, H} + Expression* e1 = (*exp->u.tuple.fields)[0].second; + frame->todo = cons(make_exp_act(e1), frame->todo); + act->pos++; + } else { + create_tuple(frame, act, exp); + } + break; + } + case GetField: { + // { { e.f :: C, E, F} :: S, H} + // -> { { e :: [].f :: C, E, F} :: S, H} + frame->todo = cons(make_lval_act(exp->u.get_field.aggregate), frame->todo); + act->pos++; + break; + } + case Variable: { + // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} + address a = lookup(exp->lineno, current_env(state), *(exp->u.variable.name), + print_error_string); + Value* v = state->heap[a]; + frame->todo = cons(make_val_act(v), frame->todo->next); + break; + } + case Integer: + // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} + frame->todo = cons(make_val_act(make_int_val(exp->u.integer)), + frame->todo->next); + break; + case Boolean: + // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} + frame->todo = cons(make_val_act(make_bool_val(exp->u.boolean)), + frame->todo->next); + break; + case PrimitiveOp: + if (exp->u.primitive_op.arguments->size() > 0) { + // { {op(e :: es) :: C, E, F} :: S, H} + // -> { e :: op([] :: es) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(exp->u.primitive_op.arguments->front()), + frame->todo); + act->pos++; + } else { + // { {v :: op(]) :: C, E, F} :: S, H} + // -> { {eval_prim(op, ()) :: C, E, F} :: S, H} + Value* v = eval_prim(exp->u.primitive_op.operator_, act->results, + exp->lineno); + frame->todo = cons(make_val_act(v), frame->todo->next->next); + } + break; + case Call: + // { {e1(e2) :: C, E, F} :: S, H} + // -> { {e1 :: [](e2) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(exp->u.call.function), frame->todo); + act->pos++; + break; + case IntT: { + Value* v = make_int_type_val(); + frame->todo = cons(make_val_act(v), frame->todo->next); + break; + } + case BoolT: { + Value* v = make_bool_type_val(); + frame->todo = cons(make_val_act(v), frame->todo->next); + break; + } + case AutoT: { + Value* v = make_auto_type_val(); + frame->todo = cons(make_val_act(v), frame->todo->next); + break; + } + case TypeT: { + Value* v = make_type_type_val(); + frame->todo = cons(make_val_act(v), frame->todo->next); + break; + } + case FunctionT: { + frame->todo = cons(make_exp_act(exp->u.function_type.parameter), + frame->todo); + act->pos++; + break; + } + } // switch (exp->tag) +} + +/***** state transitions for statements *****/ + +bool is_while_act(Action* act) { + switch (act->tag) { + case StatementAction: + switch (act->u.stmt->tag) { + case While: + return true; + default: + return false; + } + default: + return false; + } +} + +bool is_block_act(Action* act) { + switch (act->tag) { + case StatementAction: + switch (act->u.stmt->tag) { + case Block: + return true; + default: + return false; + } + default: + return false; + } +} + +void step_stmt() { + Frame* frame = state->stack->curr; + Action* act = frame->todo->curr; + Statement* stmt = act->u.stmt; + cout << "--- step stmt "; print_stmt(stmt, 1); cout << " --->" << endl; + switch (stmt->tag) { + case Match: + // { { (match (e) ...) :: C, E, F} :: S, H} + // -> { { e :: (match ([]) ...) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.match_stmt.exp), frame->todo); + act->pos++; + break; + case While: + // { { (while (e) s) :: C, E, F} :: S, H} + // -> { { e :: (while ([]) s) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.while_stmt.cond), frame->todo); + act->pos++; + break; + case Break: + // { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H} + // -> { { C, E', F} :: S, H} + frame->todo = frame->todo->next; + while (frame->todo && ! is_while_act(frame->todo->curr)) { + if (is_block_act(frame->todo->curr)) { + kill_scope(stmt->lineno, frame->scopes->curr); + frame->scopes = frame->scopes->next; + } + frame->todo = frame->todo->next; + } + frame->todo = frame->todo->next; + break; + case Continue: + // { { continue; :: ... :: (while (e) s) :: C, E, F} :: S, H} + // -> { { (while (e) s) :: C, E', F} :: S, H} + frame->todo = frame->todo->next; + while (frame->todo && ! is_while_act(frame->todo->curr)) { + if (is_block_act(frame->todo->curr)) { + kill_scope(stmt->lineno, frame->scopes->curr); + frame->scopes = frame->scopes->next; + } + frame->todo = frame->todo->next; + } + break; + case Block: { + if (act->pos == -1) { + Scope* scope = new Scope(current_env(state), list()); + frame->scopes = cons(scope, frame->scopes); + frame->todo = cons(make_stmt_act(stmt->u.block.stmt), + frame->todo); + act->pos++; + } else { + Scope* scope = frame->scopes->curr; + kill_scope(stmt->lineno, scope); + frame->scopes = frame->scopes->next; + frame->todo = frame->todo->next; + } + break; + } + case VariableDefinition: + // { {(var x = e) :: C, E, F} :: S, H} + // -> { {e :: (var x = []) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.variable_definition.init), + frame->todo); + act->pos++; + break; + case ExpressionStatement: + // { {e :: C, E, F} :: S, H} + // -> { {e :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.exp), frame->todo); + break; + case Assign: + // { {(lv = e) :: C, E, F} :: S, H} + // -> { {lv :: ([] = e) :: C, E, F} :: S, H} + frame->todo = cons(make_lval_act(stmt->u.assign.lhs), + frame->todo); + act->pos++; + break; + case If: + // { {(if (e) thn else els) :: C, E, F} :: S, H} + // -> { { e :: (if ([]) thn else els) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.if_stmt.cond), frame->todo); + act->pos++; + break; + case Return: + // { {return e :: C, E, F} :: S, H} + // -> { {e :: return [] :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.return_stmt), frame->todo); + act->pos++; + break; + case Sequence: + // { { (s1,s2) :: C, E, F} :: S, H} + // -> { { s1 :: s2 :: C, E, F} :: S, H} + Cons* todo = frame->todo->next; + if (stmt->u.sequence.next) { + todo = cons(make_stmt_act(stmt->u.sequence.next), todo); + } + frame->todo = cons(make_stmt_act(stmt->u.sequence.stmt), todo); + break; + } +} + +address get_member(address a, string f) { + vector >* fields; + Value* v = state->heap[a]; + switch (v->tag) { + case StructV: + fields = v->u.struct_val.inits->u.tuple.elts; + try { + return find_field(f, fields); + } catch (std::domain_error de) { + cerr << "runtime error, member " << f << " not in "; + print_value(v, cerr); cerr << endl; + exit(-1); + } + break; + case TupleV: + fields = v->u.tuple.elts; + try { + return find_field(f, fields); + } catch (std::domain_error de) { + cerr << "field " << f << " not in "; + print_value(v, cerr); cerr << endl; + exit(-1); + } + break; + case ChoiceTV: { + try { + find_alist(f, v->u.choice_type.alternatives); + auto ac = make_alt_cons(f, * v->u.choice_type.name); + return allocate_value(ac); + } catch (std::domain_error de) { + cerr << "alternative " << f << " not in "; + print_value(v, cerr); cerr << endl; + exit(-1); + } + break; + } + default: + cerr << "field access not allowed for value "; + print_value(v, cerr); + cerr << endl; + exit(-1); + } +} + +Cons* insert_delete(Action* del, Cons* todo) { + if (todo) { + switch (todo->curr->tag) { + case StatementAction: { + // This places the delete before the enclosing statement. + // Not sure if that is OK. Conceptually it should go after + // but that is tricky for some statements, like 'return'. -Jeremy + return cons(del, todo); + } + case LValAction: case ExpressionAction: case ValAction: + case ExpToLValAction: case DeleteTmpAction: + return cons(todo->curr, insert_delete(del, todo->next)); + } + } else { + return cons(del, todo); + } +} + +/***** State transition for handling a value *****/ + +void handle_value() { + Frame* frame = state->stack->curr; + Action* val_act = frame->todo->curr; + Action* act = frame->todo->next->curr; + act->results.push_back(val_act->u.val); + act->pos++; + + cout << "--- handle value "; print_value(val_act->u.val, cout); + cout << " with "; print_act(act, cout); cout << " --->" << endl; + + switch (act->tag) { + case DeleteTmpAction: { + kill_value(state->heap[act->u.delete_]); + frame->todo = cons(val_act, frame->todo->next->next); + break; + } + case ExpToLValAction: { + address a = allocate_value(act->results[0]); + auto del = make_delete_act(a); + frame->todo = cons(make_val_act(make_ptr_val(a)), + insert_delete(del, frame->todo->next->next)); + break; + } + case LValAction: { + Expression* exp = act->u.exp; + switch (exp->tag) { + case GetField: { + // { v :: [].f :: C, E, F} :: S, H} + // -> { { &v.f :: C, E, F} :: S, H } + Value* str = act->results[0]; + try { + address a = get_member(val_to_ptr(str, exp->lineno), + * exp->u.get_field.field); + frame->todo = cons(make_val_act(make_ptr_val(a)), + frame->todo->next->next); + } catch (std::domain_error de) { + cerr << "field " << * exp->u.get_field.field << " not in "; + print_value(str, cerr); + cerr << endl; + exit(-1); + } + break; + } + case Index: { + if (act->pos == 1) { + frame->todo = cons(make_exp_act(exp->u.index.offset), + frame->todo->next); + } else if (act->pos == 2) { + // { v :: [][i] :: C, E, F} :: S, H} + // -> { { &v[i] :: C, E, F} :: S, H } + Value* tuple = act->results[0]; + string f = std::to_string(to_integer(act->results[1])); + try { + address a = find_field(f, tuple->u.tuple.elts); + frame->todo = cons(make_val_act(make_ptr_val(a)), + frame->todo->next->next); + } catch (std::domain_error de) { + cerr << "runtime error: field " << f << "not in "; + print_value(tuple, cerr); + cerr << endl; + exit(-1); + } + } + break; + } + case Tuple: { + if (act->pos != exp->u.tuple.fields->size()) { + // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, H} + // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, H} + Expression* elt = (*exp->u.tuple.fields)[act->pos].second; + frame->todo = cons(make_lval_act(elt), frame->todo->next); + } else { + frame->todo = frame->todo->next; + create_tuple(frame, act, exp); + } + break; + } + default: + cerr << "internal error in handle_value, LValAction" << endl; + exit(-1); + } + break; + } + case ExpressionAction: { + Expression* exp = act->u.exp; + switch (exp->tag) { + case PatternVariable: { + auto v = make_var_pat_val(* exp->u.pattern_variable.name, + act->results[0]); + frame->todo = cons(make_val_act(v), frame->todo->next->next); + break; + } + case Tuple: { + if (act->pos != exp->u.tuple.fields->size()) { + // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, H} + // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, H} + Expression* elt = (*exp->u.tuple.fields)[act->pos].second; + frame->todo = cons(make_exp_act(elt), frame->todo->next); + } else { + frame->todo = frame->todo->next; + create_tuple(frame, act, exp); + } + break; + } + case Index: { + if (act->pos == 1) { + frame->todo = cons(make_exp_act(exp->u.index.offset), + frame->todo->next); + } else if (act->pos == 2) { + auto tuple = act->results[0];; + switch (tuple->tag) { + case TupleV: { + // { { v :: [][i] :: C, E, F} :: S, H} + // -> { { v_i :: C, E, F} : S, H} + string f = std::to_string(to_integer(act->results[1])); + try { + auto a = find_field(f, tuple->u.tuple.elts); + frame->todo = cons(make_val_act(state->heap[a]), + frame->todo->next->next); + } catch (std::domain_error de) { + cerr << "runtime error, field " << f + << " not in "; + print_value(tuple, cerr); + cerr << endl; + exit(-1); + } + break; + } + default: + cerr << "runtime type error, expected a tuple in field access, not "; + print_value(tuple, cerr); + exit(-1); + } + } + break; + } + case GetField: { + // { { v :: [].f :: C, E, F} :: S, H} + // -> { { v_f :: C, E, F} : S, H} + auto a = get_member(val_to_ptr(act->results[0], exp->lineno), + * exp->u.get_field.field); + frame->todo = cons(make_val_act(state->heap[a]), + frame->todo->next->next); + break; + } + case PrimitiveOp: { + if (act->pos != exp->u.primitive_op.arguments->size()) { + // { {v :: op(vs,[],e,es) :: C, E, F} :: S, H} + // -> { {e :: op(vs,v,[],es) :: C, E, F} :: S, H} + Expression* arg = (*exp->u.primitive_op.arguments)[act->pos]; + frame->todo = cons(make_exp_act(arg), frame->todo->next); + } else { + // { {v :: op(vs,[]) :: C, E, F} :: S, H} + // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H} + Value* v = eval_prim(exp->u.primitive_op.operator_, act->results, + exp->lineno); + frame->todo = cons(make_val_act(v), frame->todo->next->next); + } + break; + } + case Call: { + if (act->pos == 1) { + // { { v :: [](e) :: C, E, F} :: S, H} + // -> { { e :: v([]) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(exp->u.call.argument), + frame->todo->next); + } else if (act->pos == 2) { + // { { v2 :: v1([]) :: C, E, F} :: S, H} + // -> { {C',E',F'} :: {C, E, F} :: S, H} + frame->todo = frame->todo->next->next; + call_function(exp->lineno, act->results, state); + } else { + cerr << "internal error in handle_value with Call" << endl; + exit(-1); + } + break; + } + case FunctionT: { + if (act->pos == 2) { + // { { rt :: fn pt -> [] :: C, E, F} :: S, H} + // -> { fn pt -> rt :: {C, E, F} :: S, H} + Value* v = make_fun_type_val(act->results[0], act->results[1]); + frame->todo = cons(make_val_act(v), frame->todo->next->next); + } else { + // { { pt :: fn [] -> e :: C, E, F} :: S, H} + // -> { { e :: fn pt -> []) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(exp->u.function_type.return_type), + frame->todo->next); + } + break; + } + case Variable: case Integer: case Boolean: + case IntT: case BoolT: case TypeT: case AutoT: + cerr << "internal error, bad expression context in handle_value" << endl; + exit(-1); + } + break; + } + case StatementAction: { + Statement* stmt = act->u.stmt; + switch (stmt->tag) { + case ExpressionStatement: + frame->todo = frame->todo->next->next; + break; + case VariableDefinition: { + if (act->pos == 1) { + frame->todo = cons(make_exp_act(stmt->u.variable_definition.pat), + frame->todo->next); + } else if (act->pos == 2) { + // { { v :: (x = []) :: C, E, F} :: S, H} + // -> { { C, E(x := a), F} :: S, H(a := copy(v))} + Value* v = act->results[0]; + Value* p = act->results[1]; + //address a = allocate_value(copy_val(v)); + frame->scopes->curr->env = + pattern_match(p, v, frame->scopes->curr->env, + frame->scopes->curr->locals, stmt->lineno); + if (!frame->scopes->curr->env) { + cerr << stmt->lineno + << ": internal error in variable definition, match failed" + << endl; + exit(-1); + } + frame->todo = frame->todo->next->next; + } + break; + } + case Assign: + if (act->pos == 1) { + // { { a :: ([] = e) :: C, E, F} :: S, H} + // -> { { e :: (a = []) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(stmt->u.assign.rhs), + frame->todo->next); + } else if (act->pos == 2) { + // { { v :: (a = []) :: C, E, F} :: S, H} + // -> { { C, E, F} :: S, H(a := v)} + auto pat = act->results[0]; + auto val = act->results[1]; + pattern_assignment(pat, val, stmt->lineno); + frame->todo = frame->todo->next->next; + } + break; + case If: + if (val_to_bool(act->results[0], stmt->lineno)) { + // { {true :: if ([]) thn else els :: C, E, F} :: S, H} + // -> { { thn :: C, E, F } :: S, H} + frame->todo = cons(make_stmt_act(stmt->u.if_stmt.thn), + frame->todo->next->next); + } else { + // { {false :: if ([]) thn else els :: C, E, F} :: S, H} + // -> { { els :: C, E, F } :: S, H} + frame->todo = cons(make_stmt_act(stmt->u.if_stmt.els), + frame->todo->next->next); + } + break; + case While: + if (val_to_bool(act->results[0], stmt->lineno)) { + // { {true :: (while ([]) s) :: C, E, F} :: S, H} + // -> { { s :: (while (e) s) :: C, E, F } :: S, H} + frame->todo->next->curr->pos = -1; + frame->todo->next->curr->results.clear(); + frame->todo = cons(make_stmt_act(stmt->u.while_stmt.body), + frame->todo->next); + } else { + // { {false :: (while ([]) s) :: C, E, F} :: S, H} + // -> { { C, E, F } :: S, H} + frame->todo->next->curr->pos = -1; + frame->todo->next->curr->results.clear(); + frame->todo = frame->todo->next->next; + } + break; + case Match: { + /* + Regarding act->pos: + * odd: start interpretting the pattern of a clause + * even: finished interpretting the pattern, now try to match + + Regarding act->results: + * 0: the value that we're matching + * 1: the pattern for clause 0 + * 2: the pattern for clause 1 + * ... + */ + auto clause_num = (act->pos - 1) / 2; + if (clause_num >= stmt->u.match_stmt.clauses->size()) { + frame->todo = frame->todo->next->next; + break; + } + auto c = stmt->u.match_stmt.clauses->begin(); + std::advance(c, clause_num); + + if (act->pos % 2 == 1) { + // start interpreting the pattern of the clause + // { {v :: (match ([]) ...) :: C, E, F} :: S, H} + // -> { {pi :: (match ([]) ...) :: C, E, F} :: S, H} + frame->todo = cons(make_exp_act(c->first), frame->todo->next); + } else { // try to match + auto v = act->results[0]; + auto pat = act->results[clause_num + 1]; + auto env = current_env(state); + list vars; + Env* new_env = pattern_match(pat, v, env, vars, stmt->lineno); + if (new_env) { // we have a match, start the body + Scope* new_scope = new Scope(new_env, vars); + frame->scopes = cons(new_scope, frame->scopes); + Statement* body_block = make_block(stmt->lineno, c->second); + Action* body_act = make_stmt_act(body_block); + body_act->pos = 0; + frame->todo = cons(make_stmt_act(c->second), + cons(body_act, frame->todo->next->next)); + } else { + act->pos++; + clause_num = (act->pos - 1) / 2; + if (clause_num < stmt->u.match_stmt.clauses->size()) { + // move on to the next clause + c = stmt->u.match_stmt.clauses->begin(); + std::advance(c, clause_num); + frame->todo = cons(make_exp_act(c->first), frame->todo->next); + } else { // No more clauses in match + frame->todo = frame->todo->next->next; + } + } + } + break; + } + case Return: { + // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} + // -> { {v :: C', E', F'} :: S, H} + Value* ret_val = copy_val(val_act->u.val, stmt->lineno); + kill_locals(stmt->lineno, frame); + state->stack = state->stack->next; + frame = state->stack->curr; + frame->todo = cons(make_val_act(ret_val), frame->todo); + break; + } + case Block: + case Sequence: + case Break: + case Continue: + cerr << "internal error in handle_value, unhandled statement "; + print_stmt(stmt, 1); + cerr << endl; + exit(-1); + } // switch stmt + break; + } + case ValAction: + cerr << "internal error, ValAction in handle_value" << endl; + exit(-1); + } // switch act +} + +/***** state transition *****/ + +void step() { + Frame* frame = state->stack->curr; + if (! frame->todo) { + cerr << "runtime error: fell off end of function " << frame->name + << " without `return`" << endl; + exit(-1); + } + + Action* act = frame->todo->curr; + switch (act->tag) { + case DeleteTmpAction: + cerr << "internal error in step, did not expect DeleteTmpAction" << endl; + break; + case ExpToLValAction: + cerr << "internal error in step, did not expect ExpToLValAction" << endl; + break; + case ValAction: + handle_value(); + break; + case LValAction: + step_lvalue(); + break; + case ExpressionAction: + step_exp(); + break; + case StatementAction: + step_stmt(); + break; + } // switch +} + +/***** interpret the whole program *****/ + +int interp_program(list* fs) { + state = new State(); // runtime state + cout << "********** initializing globals **********" << endl; + init_globals(fs); + + Expression* arg = make_tuple(0, new vector >()); + Expression* call_main = make_call(0, make_var(0, "main"), arg); + Cons* todo = cons(make_exp_act(call_main), (Cons*)0); + Scope* scope = new Scope(globals, list()); + Frame* frame = new Frame("top", cons(scope, (Cons*)0), todo); + state->stack = cons(frame, (Cons*)0); + + cout << "********** calling main function **********" << endl; + print_state(cout); + + while (length(state->stack) > 1 + || length(state->stack->curr->todo) > 1 + || state->stack->curr->todo->curr->tag != ValAction) { + step(); + print_state(cout); + } + Value* v = state->stack->curr->todo->curr->u.val; + return val_to_int(v, 0); +} + +/***** interpret an expression (at compile-time) *****/ + +Value* interp_exp(Env* env, Expression* e) { + Cons* todo = cons(make_exp_act(e), (Cons*)0); + Scope* scope = new Scope(env, list()); + Frame* frame = new Frame("interp_exp", cons(scope, (Cons*)0), todo); + state->stack = cons(frame, (Cons*)0); + + while (length(state->stack) > 1 + || length(state->stack->curr->todo) > 1 + || state->stack->curr->todo->curr->tag != ValAction) { + step(); + } + Value* v = state->stack->curr->todo->curr->u.val; + return v; +} diff --git a/executable-semantics/interp.h b/executable-semantics/interp.h new file mode 100644 index 0000000000000..09606a7848030 --- /dev/null +++ b/executable-semantics/interp.h @@ -0,0 +1,131 @@ +#ifndef INTERP_H +#define INTERP_H + +#include "ast.h" +#include "assoc_list.h" +#include "cons_list.h" +#include +#include +using std::vector; +using std::list; + +struct Value; + +typedef unsigned int address; +typedef list< pair > VarValues; +typedef AList Env; + +/***** Values *****/ + +enum ValKind { IntV, FunV, PtrV, BoolV, StructV, AltV, TupleV, + VarTV, IntTV, BoolTV, TypeTV, FunctionTV, PointerTV, AutoTV, + TupleTV, StructTV, ChoiceTV, VarPatV, + AltConsV }; + +struct Value { + ValKind tag; + bool alive; + union { + int integer; + bool boolean; + struct { string* name; + Value* param; + Statement* body; + } fun; + struct { Value* type; Value* inits; } struct_val; + struct { string* alt_name; string* choice_name; } alt_cons; + struct { string* alt_name; string* choice_name; Value* arg; } alt; + struct { vector >* elts; } tuple; + address ptr; + string* var_type; + struct { string* name; Value* type; } var_pat; + struct { Value* param; Value* ret; } fun_type; + struct { Value* type; } ptr_type; + struct { string* name; VarValues* fields; VarValues* methods; } struct_type; + struct { string* name; VarValues* fields; } tuple_type; + struct { string* name; VarValues* alternatives; } choice_type; + struct { list* params; Value* type; } implicit; + } u; +}; + +Value* make_int_val(int i); +Value* make_bool_val(bool b); +Value* make_fun_val(string name, VarValues* implicit_params, Value* param, + Statement* body, vector* implicit_args); +Value* make_ptr_val(address addr); +Value* make_struct_val(Value* type, vector >* inits); +Value* make_tuple_val(vector >* elts); +Value* make_alt_val(string alt_name, string choice_name, Value* arg); +Value* make_alt_cons(string alt_name, string choice_name); + +Value* make_var_pat_val(string name, Value* type); + +Value* make_var_type_val(string name); +Value* make_int_type_val(); +Value* make_auto_type_val(); +Value* make_bool_type_val(); +Value* make_type_type_val(); +Value* make_fun_type_val(Value* param, Value* ret); +Value* make_ptr_type_val(Value* type); +Value* make_struct_type_val(string name, VarValues* fields, VarValues* methods); +Value* make_tuple_type_val(VarValues* fields); +Value* make_void_type_val(); +Value* make_choice_type_val(string* name, VarValues* alts); + +bool value_equal(Value* v1, Value* v2, int lineno); + +/***** Actions *****/ + +enum ActionKind { LValAction, ExpressionAction, StatementAction, ValAction, + ExpToLValAction, DeleteTmpAction }; + +struct Action { + ActionKind tag; + union { + Expression* exp; // for LValAction and ExpressionAction + Statement* stmt; + Value* val; // for finished actions with a value (ValAction) + address delete_; + } u; + int pos; // position or state of the action + vector results; // results from subexpression +}; +typedef Cons* ActionList; + +/***** Scopes *****/ + +struct Scope { + Scope(Env* e, const list& l) : env(e), locals(l) { } + Env* env; + list locals; +}; + +/***** Frames and State *****/ + +struct Frame { + Cons* todo; + Cons* scopes; + string name; + Frame(string n, Cons* s, Cons* c) + : name(n), scopes(s), todo(c) { } +}; + +struct State { + Cons* stack; + vector heap; +}; + +extern State* state; + +void print_value(Value* val, std::ostream& out); +void print_env(Env* env); +address allocate_value(Value* v); +Value* copy_val(Value* val, int lineno); +int to_integer(Value* v); + +/***** Interpreters *****/ + +int interp_program(list* fs); +Value* interp_exp(Env* env, Expression* e); + +#endif diff --git a/executable-semantics/syntax.l b/executable-semantics/syntax.l new file mode 100644 index 0000000000000..7622824d18dfd --- /dev/null +++ b/executable-semantics/syntax.l @@ -0,0 +1,77 @@ +%{ +#include +#include "ast.h" +#include "syntax.tab.h" +%} +%option yylineno + +integer_literal [0-9]+ +identifier [A-Za-z_][A-Za-z0-9_]* +AND "and" +OR "or" +NOT "not" +EQUAL "==" +ARROW "->" +COMMENT \/\/[^\n]*\n +RETURN "return" +IF "if" +ELSE "else" +WHILE "while" +BREAK "break" +CONTINUE "continue" +INT "Int" +BOOL "Bool" +TYPE "Type" +FN "fn" +FNTY "fnty" +TUPLETY "Tuple" +TRUE "true" +FALSE "false" +VAR "var" +STRUCT "struct" +CHOICE "choice" +MATCH "match" +CASE "case" +DBLARROW "=>" +DEFAULT "default" +AUTO "auto" +%% +{STRUCT} { return STRUCT; } +{CHOICE} { return CHOICE; } +{MATCH} { return MATCH; } +{CASE} { return CASE; } +{DBLARROW} { return DBLARROW; } +{DEFAULT} { return DEFAULT; } +{AUTO} { return AUTO; } +{AND} { return AND; } +{OR} { return OR; } +{NOT} { return NOT; } +{IF} { return IF; } +{ELSE} { return ELSE; } +{WHILE} { return WHILE; } +{BREAK} { return BREAK; } +{CONTINUE} { return CONTINUE; } +{INT} { return INT; } +{BOOL} { return BOOL; } +{TYPE} { return TYPE; } +{FN} { return FN; } +{FNTY} { return FNTY; } +{ARROW} { return ARROW; } +{VAR} { return VAR; } +{EQUAL} { return EQUAL; } +{RETURN} { return RETURN; } +{TRUE} { return TRUE; } +{FALSE} { return FALSE; } +{identifier} { + int n = strlen(yytext); + yylval.str = (char*)malloc((n + 1) * sizeof(char)); + strncpy(yylval.str, yytext, n + 1); + return identifier; + } +{integer_literal} {yylval.num = atof(yytext); return integer_literal;} +[ \t\n]+ ; +{COMMENT} ; +. {return yytext[0];} +%% +int yywrap() {return 1;} + diff --git a/executable-semantics/syntax.y b/executable-semantics/syntax.y new file mode 100644 index 0000000000000..b040e91b8873e --- /dev/null +++ b/executable-semantics/syntax.y @@ -0,0 +1,324 @@ +%{ +#include +#include +#include +#include +#include +#include "ast.h" +#include "typecheck.h" +#include "interp.h" + +extern FILE* yyin; +extern int yylineno; +char* input_filename; + +void yyerror(char*s) { + fprintf(stderr, "%s:%d: %s\n", input_filename, yylineno, s); + exit(-1); +} +// void yyerror(char *s, ...); + +extern int yylex(); +extern int yywrap(); + +//#include "typecheck.h" +//#include "eval.h" +#include +#include "ast.h" +using std::list; +using std::pair; +using std::make_pair; +using std::cout; +using std::endl; + +static list program; +%} +%union + { + char* str; + int num; + Expression* expression; + VarTypes* field_types; + Statement* statement; + Statement* statement_list; + struct FunctionDefinition* function_definition; + Declaration* declaration; + list* declaration_list; + Member* member; + list* member_list; + ExpOrFieldList* field_list; + pair* alternative; + list >* alternative_list; + pair* clause; + list< pair >* clause_list; + Expression* fun_type; +}; + +%token integer_literal +%token identifier +%type declaration +%type function_declaration +%type function_definition +%type declaration_list +%type statement +%type statement_list +%type expression +%type pattern +%type return_type +%type tuple +%type member +%type member_list +%type field +%type field_list +%type alternative +%type alternative_list +%type clause +%type clause_list +%token AND +%token OR +%token NOT +%token INT +%token BOOL +%token TYPE +%token FN +%token FNTY +%token ARROW +%token PTR +%token VAR +%token DIV +%token EQUAL +%token IF +%token ELSE +%token WHILE +%token BREAK +%token CONTINUE +%token DELETE +%token RETURN +%token TRUE +%token FALSE +%token NEW +%token STRUCT +%token CHOICE +%token MATCH +%token CASE +%token DBLARROW +%token DEFAULT +%token AUTO +%nonassoc '{' '}' +%nonassoc ':' ',' DBLARROW +%left OR AND +%nonassoc EQUAL NOT +%left '+' '-' +%left '.' ARROW +%nonassoc '(' ')' '[' ']' +%start input +%locations +%% +input: declaration_list + { + printf("********** source program **********\n"); + print_list($1, print_decl, ""); + printf("********** type checking **********\n"); + state = new State(); // compile-time state + pair p = top_level($1); + TypeEnv* top = p.first; + Env* ct_top = p.second; + list new_decls; + for (auto i = $1->begin(); i != $1->end(); ++i) { + new_decls.push_back(typecheck_decl(*i, top, ct_top)); + } + printf("\n"); + printf("********** type checking complete **********\n"); + print_list(&new_decls, print_decl, ""); + printf("********** starting execution **********\n"); + int result = interp_program(&new_decls); + cout << "result: " << result << endl; + } +; +pattern: + expression + { $$ = $1 } +; +expression: + identifier + { $$ = make_var(yylineno, $1); } +| expression '.' identifier + { $$ = make_get_field(yylineno, $1, $3); } +| expression '[' expression ']' + { $$ = make_index(yylineno, $1, $3); } +| expression ':' identifier + { $$ = make_var_pat(yylineno, $3, $1); } +| integer_literal + { $$ = make_int(yylineno, $1); } +| TRUE + { $$ = make_bool(yylineno, true); } +| FALSE + { $$ = make_bool(yylineno, false); } +| INT + { $$ = make_int_type(yylineno); } +| BOOL + { $$ = make_bool_type(yylineno); } +| TYPE + { $$ = make_type_type(yylineno); } +| AUTO + { $$ = make_auto_type(yylineno); } +| tuple { $$ = $1; } +| expression EQUAL expression + { $$ = make_binop(yylineno, Eq, $1, $3); } +| expression '+' expression + { $$ = make_binop(yylineno, Add, $1, $3); } +| expression '-' expression + { $$ = make_binop(yylineno, Sub, $1, $3); } +| expression AND expression + { $$ = make_binop(yylineno, And, $1, $3); } +| expression OR expression + { $$ = make_binop(yylineno, Or, $1, $3); } +| NOT expression + { $$ = make_unop(yylineno, Not, $2); } +| '-' expression + { $$ = make_unop(yylineno, Neg, $2); } +| expression tuple { $$ = make_call(yylineno, $1, $2); } +| FNTY tuple return_type + { $$ = make_fun_type(yylineno, $2, $3); } +; +tuple: '(' field_list ')' + { + switch ($2->tag) { + case Exp: + $$ = $2->u.exp; + break; + case FieldList: + auto vec = new vector >($2->u.fields->begin(), + $2->u.fields->end()); + $$ = make_tuple(yylineno, vec); + break; + } + } +; +field: + pattern + { $$ = make_exp($1); } +| '.' identifier '=' pattern + { auto fields = new list >(); + fields->push_back(make_pair($2, $4)); + $$ = make_field_list(fields); } +; +field_list: + /* empty */ + { $$ = make_field_list(new list >()); } +| field + { $$ = $1; } +| field ',' field_list + { $$ = cons_field($1, $3); } +; +clause: + CASE pattern DBLARROW statement + { $$ = new pair($2, $4); } +| DEFAULT DBLARROW statement + { + auto vp = make_var_pat(yylineno, "_", make_auto_type(yylineno)); + $$ = new pair(vp, $3); + } +; +clause_list: + /* empty */ + { $$ = new list< pair >(); } +| clause clause_list + { $$ = $2; $$->push_front(*$1); } +; +statement: + expression '=' expression ';' + { $$ = make_assign(yylineno, $1, $3); } +| VAR pattern '=' expression ';' + { $$ = make_var_def(yylineno, $2, $4); } +| expression ';' + { $$ = make_exp_stmt(yylineno, $1); } +| IF '(' expression ')' statement ELSE statement + { $$ = make_if(yylineno, $3, $5, $7); } +| WHILE '(' expression ')' statement + { $$ = make_while(yylineno, $3, $5); } +| BREAK ';' + { $$ = make_break(yylineno); } +| CONTINUE ';' + { $$ = make_continue(yylineno); } +| RETURN expression ';' + { $$ = make_return(yylineno, $2); } +| '{' statement_list '}' + { $$ = make_block(yylineno, $2); } +| MATCH '(' expression ')' '{' clause_list '}' + { $$ = make_match(yylineno, $3, $6); } +; +statement_list: + /* empty */ + { $$ = 0; } +| statement statement_list + { $$ = make_seq(yylineno, $1, $2); } +; +return_type: + /* empty */ + { $$ = make_tuple(yylineno, new vector >()); } +| ARROW expression + { $$ = $2; } +; +function_definition: + FN identifier tuple return_type '{' statement_list '}' + { $$ = make_fun_def(yylineno, $2, $4, $3, $6); } +| FN identifier tuple DBLARROW expression ';' + { $$ = make_fun_def(yylineno, $2, make_auto_type(yylineno), $3, + make_return(yylineno, $5)); } +; +function_declaration: + FN identifier tuple return_type ';' + { $$ = make_fun_def(yylineno, $2, $4, $3, 0); } +; +member: + VAR expression ':' identifier ';' + { $$ = make_field(yylineno, $4, $2); } +; +member_list: + /* empty */ + { $$ = new list(); } +| member member_list + { $$ = $2; $$->push_front($1); } +; +alternative: + identifier tuple ';' + { $$ = new pair($1, $2); } +; +alternative_list: + /* empty */ + { $$ = new list >(); } +| alternative alternative_list + { $$ = $2; $$->push_front(*$1); } +; +declaration: + function_definition + { $$ = make_fun_decl($1); } +| function_declaration + { $$ = make_fun_decl($1); } +| STRUCT identifier '{' member_list '}' + { $$ = make_struct_decl(yylineno, $2, $4); } +| CHOICE identifier '{' alternative_list '}' + { $$ = make_choice_decl(yylineno, $2, $4); } +; +declaration_list: + /* empty */ + { $$ = new list(); } +| declaration declaration_list + { $$ = $2; $$->push_front($1); } +; +%% +int main(int argc, char* argv[]) { + /*yydebug = 1;*/ + + if (argc > 1) { + input_filename = argv[1]; + yyin = fopen(argv[1], "r"); + } + if (argc > 2) { + FILE* program = fopen(argv[2], "r"); + input = read_file(program); + } + yyparse(); + return 0; +} diff --git a/executable-semantics/typecheck.cc b/executable-semantics/typecheck.cc new file mode 100644 index 0000000000000..724fd3d5ce53f --- /dev/null +++ b/executable-semantics/typecheck.cc @@ -0,0 +1,862 @@ +#include "typecheck.h" +#include "interp.h" +#include "cons_list.h" +#include +#include +#include +#include +using std::vector; +using std::set; +using std::map; +using std::cerr; +using std::cout; +using std::endl; +using std::make_pair; + +template +bool list_equal(list* ts1, list* ts2, bool(*eq)(T*,T*)) { + if (ts1->size() == ts2->size()) { + auto iter2 = ts2->begin(); + for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1, ++iter2) { + if (! eq(*iter1, *iter2)) + return false; + } + return true; + } else { + return false; + } +} + +template +bool vector_equal(vector* ts1, vector* ts2, bool(*eq)(T*,T*)) { + if (ts1->size() == ts2->size()) { + auto iter2 = ts2->begin(); + for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1, ++iter2) { + if (! eq(*iter1, *iter2)) + return false; + } + return true; + } else { + return false; + } +} + +bool fields_equal(VarValues* ts1, VarValues* ts2) { + if (ts1->size() == ts2->size()) { + for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1) { + try { + auto t2 = find_alist(iter1->first, ts2); + if (! type_equal(iter1->second, t2)) + return false; + } catch (std::domain_error de) { + return false; + } + } + return true; + } else { + return false; + } +} + +int find(const string& s, Cons* ls, int n) { + if (ls) { + if (ls->curr == s) + return n; + else + return find(s, ls->next, n + 1); + } else { + cerr << "could not find " << s << endl; + exit(-1); + } +} + +bool type_equal( Value* t1, Value* t2) { + return (t1->tag == VarTV && t2->tag == VarTV + && *t1->u.var_type == *t2->u.var_type) + || (t1->tag == IntTV && t2->tag == IntTV) + || (t1->tag == BoolTV && t2->tag == BoolTV) + || (t1->tag == PointerTV && t2->tag == PointerTV + && type_equal(t1->u.ptr_type.type, t2->u.ptr_type.type)) + || (t1->tag == FunctionTV && t2->tag == FunctionTV + && type_equal(t1->u.fun_type.param, t2->u.fun_type.param) + && type_equal(t1->u.fun_type.ret, t2->u.fun_type.ret)) + || (t1->tag == StructTV && t2->tag == StructTV + && *t1->u.struct_type.name == *t2->u.struct_type.name) + || (t1->tag == ChoiceTV && t2->tag == ChoiceTV + && *t1->u.choice_type.name == *t2->u.choice_type.name) + || (t1->tag == TupleTV && t2->tag == TupleTV + && fields_equal(t1->u.tuple_type.fields, + t2->u.tuple_type.fields)); +} + +void expect_type(int lineno, string context, + Value* expected, Value* actual) { + if (! type_equal(expected, actual)) { + cerr << lineno << ": type error in " << context << endl; + cerr << "expected: "; + print_value(expected, cerr); + cerr << endl << "actual: "; + print_value(actual, cerr); + cerr << endl; + exit(-1); + } +} + +void print_error_string(string s) { + cerr << s; +} + +void print_type_env( TypeEnv* env, std::ostream& out) { + if (env) { + out << env->key << ": "; + print_value(env->value, out); + out << ", "; + print_type_env(env->next, out); + } +} + +void match_types(int lineno, Value* param, Value* arg, + map& tyvar_map) { + switch (param->tag) { + case TupleTV: + if (param->u.tuple_type.fields->size() != arg->u.tuple_type.fields->size()){ + expect_type(lineno, "arity mismatch", param, arg); + exit(-1); + } + for (auto i = param->u.tuple_type.fields->begin(); + i != param->u.tuple_type.fields->end(); + ++i) { + try { + auto argT = find_alist(i->first, arg->u.tuple_type.fields); + match_types(lineno, i->second, argT, tyvar_map); + } catch (std::domain_error de) { + cerr << "missing field " << i->first << endl; + exit(-1); + } + } + break; + case ChoiceTV: + expect_type(lineno, "function call", param, arg); + break; + case StructTV: + expect_type(lineno, "function call", param, arg); + break; + case TypeTV: + expect_type(lineno, "function call", param, arg); + break; + case VarTV: + if (tyvar_map.count(*param->u.var_type) == 0) { + tyvar_map[*param->u.var_type] = arg; + } else { + expect_type(lineno, "call to generic function", + tyvar_map[*param->u.var_type], arg); + } + break; + case BoolTV: + expect_type(lineno, "function call", param, arg); + break; + case IntTV: + expect_type(lineno, "function call", param, arg); + break; + case PointerTV: + switch (arg->tag) { + case PointerTV: + match_types(lineno, param->u.ptr_type.type, arg->u.ptr_type.type, + tyvar_map); + break; + default: + cerr << "expected argument to be a pointer" << endl; + expect_type(lineno, "function call", param, arg); + exit(-1); + } + break; + case FunctionTV: + switch (arg->tag) { + case FunctionTV: + match_types(lineno, param->u.fun_type.param, arg->u.fun_type.param, + tyvar_map); + match_types(lineno, param->u.fun_type.ret, arg->u.fun_type.ret, + tyvar_map); + break; + default: + cerr << "expected argument to be a function" << endl; + expect_type(lineno, "function call", param, arg); + exit(-1); + } + break; + default: + cerr << "in match_type, expected a type, not "; + print_value(param, cerr); + cerr << endl; + exit(-1); + } +} + +Value* subst_type(Value* t, map& tyvar_map) { + switch (t->tag) { + case ChoiceTV: + return t; // update when choices get type parameters + case StructTV: + return t; // update when structs get type parameters + case TypeTV: + return t; + case VarTV: + return tyvar_map[* t->u.var_type]; + case BoolTV: + return t; + case IntTV: + return t; + case PointerTV: + return make_ptr_type_val(subst_type(t->u.ptr_type.type, tyvar_map)); + case FunctionTV: { + return make_fun_type_val(subst_type(t->u.fun_type.param, tyvar_map), + subst_type(t->u.fun_type.ret, tyvar_map)); + } + case TupleTV: { + auto fields = new VarValues(); + for (auto i = t->u.tuple_type.fields->begin(); + i != t->u.tuple_type.fields->end(); ++i) { + fields->push_back(make_pair(i->first, + subst_type(i->second, + tyvar_map))); + } + return make_tuple_type_val(fields); + } + default: + cerr << "in subst_type, expected a type " << endl; + exit(-1); + } +} + + +// Convert tuples to tuple types. +Value* to_type(int lineno, Value* val) { + switch (val->tag) { + case TupleV: { + auto fields = new VarValues(); + for (auto i = val->u.tuple.elts->begin(); + i != val->u.tuple.elts->end(); ++i) { + Value* ty = to_type(lineno, state->heap[i->second]); + fields->push_back(make_pair(i->first, ty)); + } + return make_tuple_type_val(fields); + } + case TupleTV: { + auto fields = new VarValues(); + for (auto i = val->u.tuple_type.fields->begin(); + i != val->u.tuple_type.fields->end(); ++i) { + Value* ty = to_type(lineno, i->second); + fields->push_back(make_pair(i->first, ty)); + } + return make_tuple_type_val(fields); + } + case PointerTV: { + return make_ptr_type_val(to_type(lineno, val->u.ptr_type.type)); + } + case FunctionTV: { + return make_fun_type_val + (to_type(lineno, val->u.fun_type.param), + to_type(lineno, val->u.fun_type.ret)); + } + case VarPatV: { + return make_var_pat_val(* val->u.var_pat.name, + to_type(lineno, val->u.var_pat.type)); + } + case ChoiceTV: + case StructTV: + case TypeTV: + case VarTV: + case BoolTV: + case IntTV: + case AutoTV: + return val; + default: + cerr << lineno << ": in to_type, expected a type, not "; + print_value(val, cerr); + cerr << endl; + exit(-1); + } +} + +// Reify type to type expression. +Expression* reify_type(Value* t, int lineno) { + switch (t->tag) { + case VarTV: + return make_var(0, *t->u.var_type); + case IntTV: + return make_int_type(0); + case BoolTV: + return make_bool_type(0); + case TypeTV: + return make_type_type(0); + case FunctionTV: + return make_fun_type(0, reify_type(t->u.fun_type.param, lineno), + reify_type(t->u.fun_type.ret, lineno)); + case TupleTV: { + auto args = new vector >(); + for (auto i = t->u.tuple_type.fields->begin(); + i != t->u.tuple_type.fields->end(); ++i) { + args->push_back(make_pair(i->first, reify_type(i->second, lineno))); + } + return make_tuple(0, args); + } + case StructTV: + return make_var(0, *t->u.struct_type.name); + case ChoiceTV: + return make_var(0, *t->u.choice_type.name); + default: + cerr << lineno << ": expected a type, not "; print_value(t, cerr); + cerr << endl; + exit(-1); + } +} + + +TCResult typecheck_exp(Expression* e, TypeEnv* env, Env* ct_env, + Value* expected, TCContext context) +{ // expected can be null + switch (e->tag) { + case PatternVariable: { + if (context != PatternContext) { + cerr << e->lineno + << ": compilation error, pattern variables are only allowed in pattern context" + << endl; + } + auto t = to_type(e->lineno, + interp_exp(ct_env, e->u.pattern_variable.type)); + if (t->tag == AutoTV) { + if (expected == 0) { + cerr << e->lineno << ": compilation error, auto not allowed here" << endl; + exit(-1); + } else { + t = expected; + } + } + auto new_e = make_var_pat(e->lineno, *e->u.pattern_variable.name, + reify_type(t, e->lineno)); + return TCResult(new_e, t, + new TypeEnv(* e->u.pattern_variable.name, t, env)); + } + case Index: { + auto res = typecheck_exp(e->u.get_field.aggregate, env, ct_env, 0, + ValueContext); + auto t = res.type; + switch (t->tag) { + case TupleTV: { + auto i = to_integer(interp_exp(ct_env, e->u.index.offset)); + string f = std::to_string(i); + try { + auto fieldT = find_alist(f, t->u.tuple_type.fields); + auto new_e = make_index(e->lineno, res.exp, make_int(e->lineno, i)); + return TCResult(new_e, fieldT, res.env); + } catch (std::domain_error de) { + cerr << e->lineno << ": compilation error, field " << f + << " is not in the tuple "; + print_value(t, cerr); + cerr << endl; + } + } + default: + cerr << e->lineno << ": compilation error, expected a tuple" << endl; + exit(-1); + } + } + case Tuple: { + auto new_args = new vector >(); + auto arg_types = new VarValues(); + auto new_env = env; + int i = 0; + for (auto arg = e->u.tuple.fields->begin(); + arg != e->u.tuple.fields->end(); ++arg, ++i) { + Value* arg_expected = 0; + if (expected && expected->tag == TupleTV) { + try { + arg_expected = find_alist(arg->first, expected->u.tuple_type.fields); + } catch (std::domain_error de) { + cerr << e->lineno << ": compilation error, missing field " + << arg->first << endl; + exit(-1); + } + } + auto arg_res = typecheck_exp(arg->second, new_env, ct_env, + arg_expected, context); + new_env = arg_res.env; + new_args->push_back(make_pair(arg->first, arg_res.exp)); + arg_types->push_back(make_pair(arg->first, arg_res.type)); + } + auto tupleE = make_tuple(e->lineno, new_args); + auto tupleT = make_tuple_type_val(arg_types); + return TCResult(tupleE, tupleT, new_env); + } + case GetField: { + auto res = typecheck_exp(e->u.get_field.aggregate, env, ct_env, 0, + ValueContext); + auto t = res.type; + switch (t->tag) { + case StructTV: + // Search for a field + for (auto vt = t->u.struct_type.fields->begin(); + vt != t->u.struct_type.fields->end(); ++vt) { + if (*e->u.get_field.field == vt->first) { + Expression* new_e = make_get_field(e->lineno, res.exp, + *e->u.get_field.field); + return TCResult(new_e, vt->second, res.env); + } + } + // Search for a method + for (auto vt = t->u.struct_type.methods->begin(); + vt != t->u.struct_type.methods->end(); ++vt) { + if (*e->u.get_field.field == vt->first) { + Expression* new_e = make_get_field(e->lineno, res.exp, + *e->u.get_field.field); + return TCResult(new_e, vt->second, res.env); + } + } + cerr << e->lineno << ": compilation error, struct " + << * t->u.struct_type.name + << " does not have a field named " << * e->u.get_field.field + << endl; + exit(-1); + case TupleTV: + for (auto vt = t->u.tuple_type.fields->begin(); + vt != t->u.tuple_type.fields->end(); ++vt) { + if (*e->u.get_field.field == vt->first) { + auto new_e = make_get_field(e->lineno, res.exp, + *e->u.get_field.field); + return TCResult(new_e, vt->second, res.env); + } + } + cerr << e->lineno << ": compilation error, struct " + << * t->u.struct_type.name + << " does not have a field named " << * e->u.get_field.field + << endl; + exit(-1); + case ChoiceTV: + for (auto vt = t->u.choice_type.alternatives->begin(); + vt != t->u.choice_type.alternatives->end(); ++vt) { + if (*e->u.get_field.field == vt->first) { + Expression* new_e = make_get_field(e->lineno, res.exp, + *e->u.get_field.field); + auto fun_ty = make_fun_type_val(vt->second, t); + return TCResult(new_e, fun_ty, res.env); + } + } + cerr << e->lineno << ": compilation error, struct " + << * t->u.struct_type.name + << " does not have a field named " << * e->u.get_field.field + << endl; + exit(-1); + + default: + cerr << e->lineno + << ": compilation error in field access, expected a struct" + << endl; + print_exp(e); + cerr << endl; + exit(-1); + } + } + case Variable: { + auto t = lookup(e->lineno, env, *(e->u.variable.name), print_error_string); + return TCResult(e, t, env); + } + case Integer: + return TCResult(e, make_int_type_val(), env); + break; + case Boolean: + return TCResult(e, make_bool_type_val(), env); + break; + case PrimitiveOp: { + auto es = new vector(); + vector ts; + auto new_env = env; + for (auto iter = e->u.primitive_op.arguments->begin(); + iter != e->u.primitive_op.arguments->end(); ++iter) { + auto res = typecheck_exp(*iter, env, ct_env, 0, ValueContext); + new_env = res.env; + es->push_back(res.exp); + ts.push_back(res.type); + } + auto new_e = make_op(e->lineno, e->u.primitive_op.operator_, es); + switch (e->u.primitive_op.operator_) { + case Neg: + expect_type(e->lineno, "negation", make_int_type_val(), ts[0]); + return TCResult(new_e, make_int_type_val(), new_env); + case Add: + case Sub: + expect_type(e->lineno, "subtraction(1)", make_int_type_val(), ts[0]); + expect_type(e->lineno, "substration(2)", make_int_type_val(), ts[1]); + return TCResult(new_e, make_int_type_val(), new_env); + case And: + expect_type(e->lineno, "&&(1)", make_bool_type_val(), ts[0]); + expect_type(e->lineno, "&&(2)", make_bool_type_val(), ts[1]); + return TCResult(new_e, make_bool_type_val(), new_env); + case Or: + expect_type(e->lineno, "||(1)", make_bool_type_val(), ts[0]); + expect_type(e->lineno, "||(2)", make_bool_type_val(), ts[1]); + return TCResult(new_e, make_bool_type_val(), new_env); + case Not: + expect_type(e->lineno, "!", make_bool_type_val(), ts[0]); + return TCResult(new_e, make_bool_type_val(), new_env); + case Eq: + expect_type(e->lineno, "==(1)", make_int_type_val(), ts[0]); + expect_type(e->lineno, "==(2)", make_int_type_val(), ts[1]); + return TCResult(new_e, make_bool_type_val(), new_env); + } + break; + } + case Call: { + auto fun_res = typecheck_exp(e->u.call.function, env, ct_env, 0, + ValueContext); + switch (fun_res.type->tag) { + case FunctionTV: { + auto funT = fun_res.type; + auto arg_res = typecheck_exp(e->u.call.argument, + fun_res.env, ct_env, + funT->u.fun_type.param, + ValueContext); + expect_type(e->lineno, "call", funT->u.fun_type.param, arg_res.type); + auto new_e = make_call(e->lineno, fun_res.exp, arg_res.exp); + return TCResult(new_e, funT->u.fun_type.ret, arg_res.env); + } + default: { + cerr << e->lineno << ": compilation error in call, expected a function" + << endl; + print_exp(e); + cerr << endl; + exit(-1); + } + } + break; + } + case FunctionT: { + switch (context) { + case ValueContext: + case TypeContext: { + auto pt = to_type(e->lineno, + interp_exp(ct_env, e->u.function_type.parameter)); + auto rt = to_type(e->lineno, + interp_exp(ct_env, e->u.function_type.return_type)); + auto new_e = make_fun_type(e->lineno, reify_type(pt, e->lineno), + reify_type(rt, e->lineno)); + return TCResult(new_e, make_type_type_val(), env); + } + case PatternContext: { + auto param_res = typecheck_exp(e->u.function_type.parameter, env, ct_env, + 0, context); + auto ret_res = typecheck_exp(e->u.function_type.return_type, + param_res.env, ct_env, 0, context); + auto new_e = make_fun_type(e->lineno, reify_type(param_res.type, + e->lineno), + reify_type(ret_res.type, e->lineno)); + return TCResult(new_e, make_type_type_val(), ret_res.env); + } + } + } + case IntT: case BoolT: case TypeT: case AutoT: + return TCResult(e, make_type_type_val(), env); + } +} + +pair +typecheck_case( Value* expected, Expression* pat, + Statement* body, TypeEnv* env, Env* ct_env, + Value* ret_type) { + auto pat_res = typecheck_exp(pat, env, ct_env, expected, PatternContext); + auto res = typecheck_stmt(body, pat_res.env, ct_env, ret_type); + return make_pair(pat, res.stmt); +} + +TCStatement +typecheck_stmt(Statement* s, TypeEnv* env, Env* ct_env, + Value* ret_type) { + if (! s) { + return TCStatement(s, env); + } + switch (s->tag) { + case Match: { + auto res = typecheck_exp(s->u.match_stmt.exp, env, ct_env, 0, ValueContext); + auto res_type = res.type; + auto new_clauses = new list< pair >(); + for (auto i = s->u.match_stmt.clauses->begin(); + i != s->u.match_stmt.clauses->end(); ++i) { + new_clauses->push_back(typecheck_case(res_type, + i->first, i->second, + env, ct_env, ret_type)); + } + Statement* new_s = make_match(s->lineno, res.exp, new_clauses); + return TCStatement(new_s, env); + } + case While: { + auto cnd_res = typecheck_exp(s->u.while_stmt.cond, env, ct_env, 0, + ValueContext); + expect_type(s->lineno, "condition of `while`", make_bool_type_val(), + cnd_res.type); + auto body_res = typecheck_stmt(s->u.while_stmt.body, + env, ct_env, ret_type); + auto new_s = make_while(s->lineno, cnd_res.exp, body_res.stmt); + return TCStatement(new_s, env); + } + case Break: + return TCStatement(s, env); + case Continue: + return TCStatement(s, env); + case Block: { + auto stmt_res = typecheck_stmt(s->u.block.stmt, + env, ct_env, ret_type); + return TCStatement(make_block(s->lineno, stmt_res.stmt), env); + } + case VariableDefinition: { + auto res = typecheck_exp(s->u.variable_definition.init, + env, ct_env, 0, ValueContext); + Value* rhs_ty = res.type; + auto lhs_res = typecheck_exp(s->u.variable_definition.pat, + env, ct_env, rhs_ty, PatternContext); + Statement* new_s = make_var_def(s->lineno, s->u.variable_definition.pat, + res.exp); + return TCStatement(new_s, lhs_res.env); + } + case Sequence: { + auto stmt_res = typecheck_stmt(s->u.sequence.stmt, env, ct_env, + ret_type); + auto env2 = stmt_res.env; + auto next_res = typecheck_stmt(s->u.sequence.next, env2, ct_env, + ret_type); + auto env3 = next_res.env; + return TCStatement(make_seq(s->lineno, stmt_res.stmt, next_res.stmt), env3); + } + case Assign: { + auto rhs_res = typecheck_exp(s->u.assign.rhs, env, ct_env, 0, ValueContext); + auto rhsT = rhs_res.type; + auto lhs_res = typecheck_exp(s->u.assign.lhs, env, ct_env, rhsT, + ValueContext); + auto lhsT = lhs_res.type; + expect_type(s->lineno, "assign", lhsT, rhsT); + auto new_s = make_assign(s->lineno, lhs_res.exp, rhs_res.exp); + return TCStatement(new_s, lhs_res.env); + } + case ExpressionStatement: { + auto res = typecheck_exp(s->u.exp, env, ct_env, 0, ValueContext); + auto new_s = make_exp_stmt(s->lineno, res.exp); + return TCStatement(new_s, env); + } + case If: { + auto cnd_res = typecheck_exp(s->u.if_stmt.cond, env, ct_env, 0, + ValueContext); + expect_type(s->lineno, "condition of `if`", make_bool_type_val(), + cnd_res.type); + auto thn_res = typecheck_stmt(s->u.if_stmt.thn, env, ct_env, + ret_type); + auto els_res = typecheck_stmt(s->u.if_stmt.els, env, ct_env, + ret_type); + auto new_s = make_if(s->lineno, cnd_res.exp, thn_res.stmt, els_res.stmt); + return TCStatement(new_s, env); + } + case Return: { + auto res = typecheck_exp(s->u.return_stmt, env, ct_env, 0, ValueContext); + if (ret_type->tag == AutoTV) { + // The following infers the return type from the first 'return' statement. + // This will get more difficult with subtyping, when we should infer + // the least-upper bound of all the 'return' statements. + *ret_type = *res.type; + } else { + expect_type(s->lineno, "return", ret_type, res.type); + } + return TCStatement(make_return(s->lineno, res.exp), env); + } + } +} + + +Statement* check_or_ensure_return(Statement* stmt, bool void_return, + int lineno) { + if (! stmt) { + if (void_return) { + auto args = new vector >(); + return make_return(lineno, make_tuple(lineno, args)); + } else { + cerr << "control-flow reaches end of non-void function without a return" + << endl; + exit(-1); + } + } + switch (stmt->tag) { + case Match: { + auto new_clauses = new list< pair >(); + for (auto i = stmt->u.match_stmt.clauses->begin(); + i != stmt->u.match_stmt.clauses->end(); ++i) { + auto s = check_or_ensure_return(i->second, void_return, stmt->lineno); + new_clauses->push_back(make_pair(i->first, s)); + } + return make_match(stmt->lineno, stmt->u.match_stmt.exp, new_clauses); + } + case Block: + return make_block(stmt->lineno, + check_or_ensure_return(stmt->u.block.stmt, void_return, + stmt->lineno)); + case If: + return make_if(stmt->lineno, stmt->u.if_stmt.cond, + check_or_ensure_return(stmt->u.if_stmt.thn, void_return, + stmt->lineno), + check_or_ensure_return(stmt->u.if_stmt.els, void_return, + stmt->lineno)); + case Return: + return stmt; + case Sequence: + if (stmt->u.sequence.next) { + return make_seq(stmt->lineno, stmt->u.sequence.stmt, + check_or_ensure_return(stmt->u.sequence.next, + void_return, stmt->lineno)); + } else { + return check_or_ensure_return(stmt->u.sequence.stmt, void_return, + stmt->lineno); + } + case Assign: + case ExpressionStatement: + case While: + case Break: + case Continue: + case VariableDefinition: + if (void_return) { + auto args = new vector >(); + return make_seq(stmt->lineno, stmt, + make_return(stmt->lineno, + make_tuple(stmt->lineno, args))); + } else { + cerr << stmt->lineno + << ": control-flow reaches end of non-void function without a return" + << endl; + exit(-1); + } + } +} + +struct FunctionDefinition* +typecheck_fun_def(struct FunctionDefinition* f, + TypeEnv* env, Env* ct_env) { + auto param_res = typecheck_exp(f->param_pattern, env, ct_env, 0, + PatternContext); + auto return_type = to_type(f->lineno, interp_exp(ct_env, f->return_type)); + if (f->name == "main") { + expect_type(f->lineno, "return type of `main`", + make_int_type_val(), return_type); + // todo: check that main doesn't have any parameters + } + auto res = typecheck_stmt(f->body, param_res.env, ct_env, return_type); + bool void_return = type_equal(return_type, make_void_type_val()); + auto body = check_or_ensure_return(res.stmt, void_return, f->lineno); + return make_fun_def(f->lineno, f->name, reify_type(return_type, f->lineno), + f->param_pattern, body); +} + +Value* type_of_fun_def( TypeEnv* env, Env* ct_env, + struct FunctionDefinition* fun_def) { + auto param_res = typecheck_exp(fun_def->param_pattern, env, ct_env, 0, + PatternContext); + auto param_type = to_type(fun_def->lineno, param_res.type); + auto ret = interp_exp(ct_env, fun_def->return_type); + if (ret->tag == AutoTV) { + auto f = typecheck_fun_def(fun_def, env, ct_env); + ret = interp_exp(ct_env, f->return_type); + } + return make_fun_type_val(param_type, ret); +} + +Value* type_of_struct_def(struct StructDefinition* sd, TypeEnv* env, Env* ct_top) { + auto fields = new VarValues(); + auto methods = new VarValues(); + for (auto m = sd->members->begin(); + m != sd->members->end(); ++m) { + if ((*m)->tag == FieldMember) { + auto t = to_type(sd->lineno, + interp_exp(ct_top, (*m)->u.field.type)); + fields->push_back(make_pair(* (*m)->u.field.name, t)); + } + } + return make_struct_type_val(* sd->name, fields, methods); +} + +string name_of_decl(Declaration* d) { + switch (d->tag) { + case FunctionDeclaration: + return d->u.fun_def->name; + case StructDeclaration: + return * d->u.struct_def->name; + case ChoiceDeclaration: + return * d->u.choice_def.name; + } +} + +Declaration* typecheck_decl(Declaration* d, TypeEnv* env, + Env* ct_env) { + switch (d->tag) { + case StructDeclaration: { + auto struct_type = type_of_struct_def(d->u.struct_def, env, ct_env); + auto members = new list(); + for (auto m = d->u.struct_def->members->begin(); + m != d->u.struct_def->members->end(); ++m) { + switch ((*m)->tag) { + case FieldMember: { + // TODO interpret the type expression and store the result. + members->push_back(*m); + break; + } + } + } + return make_struct_decl(d->u.struct_def->lineno, + *d->u.struct_def->name, members); + } + case FunctionDeclaration: + return make_fun_decl(typecheck_fun_def(d->u.fun_def, env, ct_env)); + case ChoiceDeclaration: + return d; // TODO + } +} + + +pair top_level(list* fs) { + TypeEnv* top = 0; + Env* ct_top = 0; + bool found_main = false; + for (auto i = fs->begin(); i != fs->end(); ++i) { + auto d = *i; + if (name_of_decl(d) == "main") { + found_main = true; + } + switch (d->tag) { + case FunctionDeclaration: { + auto t = type_of_fun_def(top, ct_top, d->u.fun_def); + top = new TypeEnv(name_of_decl(d), t, top); + break; + } + case StructDeclaration: { + auto st = type_of_struct_def(d->u.struct_def, top, ct_top); + address a = allocate_value(st); + ct_top = new Env(name_of_decl(d), a, ct_top); // is this obsolete? + auto params = make_tuple_type_val(st->u.struct_type.fields); + auto fun_ty = make_fun_type_val(params, st); + top = new TypeEnv(name_of_decl(d), fun_ty, top); + break; + } + case ChoiceDeclaration: { + auto alts = new VarValues(); + for (auto i = d->u.choice_def.alternatives->begin(); + i != d->u.choice_def.alternatives->end(); ++i) { + auto t = to_type(d->u.choice_def.lineno, + interp_exp(ct_top, i->second)); + alts->push_back(make_pair(i->first, t)); + } + auto ct = make_choice_type_val(d->u.choice_def.name, alts); + address a = allocate_value(ct); + ct_top = new Env(name_of_decl(d), a, ct_top); // is this obsolete? + top = new TypeEnv(name_of_decl(d), ct, top); + break; + } + } // switch (d->tag) + } // for + if (found_main == false) { + cerr << "error, program must contain a function named `main`" << endl; + exit(-1); + } + return make_pair(top, ct_top); +} diff --git a/executable-semantics/typecheck.h b/executable-semantics/typecheck.h new file mode 100644 index 0000000000000..fdeee72976216 --- /dev/null +++ b/executable-semantics/typecheck.h @@ -0,0 +1,50 @@ +#ifndef TYPECHECK_H +#define TYPECHECK_H + +#include +using std::set; +using std::pair; +#include "ast.h" +#include "assoc_list.h" +#include "interp.h" + +typedef AList TypeEnv; + +void print_type_env(TypeEnv* env); + +enum TCContext { ValueContext, PatternContext, TypeContext }; + +struct TCResult { + TCResult(Expression* e, Value* t, TypeEnv* env) + : exp(e), type(t), env(env) { } + Expression* exp; + Value* type; + TypeEnv* env; +}; + +struct TCStatement { + TCStatement(Statement* s, TypeEnv* e) : stmt(s), env(e) { } + Statement* stmt; + TypeEnv* env; +}; + +Value* to_type(int lineno, Value* val); + +bool type_equal(Value* t1, Value* t2); +bool fields_equal(VarValues* ts1, VarValues* ts2); + +TCResult typecheck_exp(Expression* e, TypeEnv* env, Env* ct_env, + Value* expected, TCContext context); + +TCStatement typecheck_stmt(Statement*, TypeEnv*, Env*, Value*); + +struct FunctionDefinition* +typecheck_fun_def(struct FunctionDefinition*, TypeEnv*); + +Declaration* typecheck_decl(Declaration* d, TypeEnv* env, Env* ct_env); + +pair top_level(list* fs); + +void print_error_string(string s); + +#endif diff --git a/proposals/p0162.md b/proposals/p0162.md index 5ebfcc4e70b65..2282a521e5283 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -276,9 +276,9 @@ ordered instead of being totally ordered. nonassoc '{' '}' nonassoc ':' ',' - right "or" "and" + left "or" "and" nonassoc "==" "not" - right '+' '-' + left '+' '-' left '.' "->" nonassoc '(' ')' '[' ']' @@ -292,7 +292,7 @@ the semantic analysis and the specification of runtime behavior. #### Abstract Syntax for Expressions ```C++ -enum ExpressionKind { Variable, PatternVariable, Dereference, Int, Bool, +enum ExpressionKind { Variable, PatternVariable, Int, Bool, PrimitiveOp, Call, Tuple, Index, GetField, IntT, BoolT, TypeT, FunctionT, AutoT }; enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; @@ -348,8 +348,8 @@ created containing the parse results for the fields. ```C++ enum StatementKind { ExpressionStatement, Assign, VariableDefinition, - Delete, If, Return, Sequence, Block, While, Break, - Continue, Match }; + If, Return, Sequence, Block, While, Break, Continue, + Match }; struct Statement { StatementKind tag; From 0e38274a390735fbea5ab20458cb66b7acc326a6 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sat, 24 Oct 2020 09:42:30 -0400 Subject: [PATCH 48/56] update README --- executable-semantics/README.md | 209 +++++++++++++++------------------ 1 file changed, 97 insertions(+), 112 deletions(-) diff --git a/executable-semantics/README.md b/executable-semantics/README.md index 05ad154f9b9f9..afae9c0390838 100644 --- a/executable-semantics/README.md +++ b/executable-semantics/README.md @@ -1,148 +1,133 @@ # Executable Semantics -This directory contains a work-in-progress executable semantics. It -started as an executable semantics for Featherweight C and it is -migrating into an executable semantics for the Carbon language. It -includes a parser, type checker, and abstract machine. +This directory contains a work-in-progress executable semantics. It started as +an executable semantics for Featherweight C and it is migrating into an +executable semantics for the Carbon language. It includes a parser, type +checker, and abstract machine. -This language currently includes several kinds of values: integer, -booleans, pointers, functions, and structs. A kind of safe union, -called a `choice`, is in progress. Regarding control-flow, it includes -if statements, while loops, break, continue, function calls, and a -variant of `switch` called `match` is in progress. +This language currently includes several kinds of values: integer, booleans, +functions, and structs. A kind of safe union, called a `choice`, is in +progress. Regarding control-flow, it includes if statements, while loops, break, +continue, function calls, and a variant of `switch` called `match` is in +progress. -The parser is implemented using the flex and bison parser generator -tools. +The grammar of the language matches the one in Proposal +[#162](https://github.com/carbon-language/carbon-lang/pull/162). The +type checker and abstract machine do not yet have a corresponding +proposal. Nevertheless they are present here to help test the parser +but should not be considered definitive. -* [`syntax.l`](./syntax.l) the lexer specification -* [`syntax.y`](./syntax.y) the grammar +The parser is implemented using the flex and bison parser generator tools. + +- [`syntax.l`](./syntax.l) the lexer specification +- [`syntax.y`](./syntax.y) the grammar The parser translates program text into an abstract syntax tree (AST). -* [`ast.h`](./ast.h) includes structure definitions for the AST and function - declarations for creating and printing ASTs. -* [`ast.cc`](./ast.cc) contains the function definitions. +- [`ast.h`](./ast.h) includes structure definitions for the AST and function + declarations for creating and printing ASTs. +- [`ast.cc`](./ast.cc) contains the function definitions. -The type checker defines what it means for an AST to be a valid -program. The type checker prints an error and exits if the AST is -invalid. +The type checker defines what it means for an AST to be a valid program. The +type checker prints an error and exits if the AST is invalid. -* [`typecheck.h`](./typecheck.h) -* [`typecheck.cc`](./typecheck.cc) +- [`typecheck.h`](./typecheck.h) +- [`typecheck.cc`](./typecheck.cc) The parser and type checker together specify the static (compile-time) semantics. -The dynamic (run-time) semantics is specified by an abstract -machine. Abstract machines have several positive characteristics that -make them good for specification: - -* abstract machines operate on the AST of the program - (and not some lower-level representation such as bytecode) - so they directly connect the program to its behavior - -* abstract machines can easily handle language features with complex - control-flow, such as goto, exceptions, coroutines, and even - first-class continuations. - -The one down-side of abstract machines is that they are not as simple -as a definitional interpreter (a recursive function that interprets -the program), but it is more difficult to handle complex control flow in -a definitional interpreter. - -* [`interp.h`](./interp.h) declares the `interp_program` function. -* [`interp.cc`](./interp.cc) implements `interp_program` function using an - abstract machine, as described below. - -The abstract machine implements a state-transition system. The state -is defined by the `State` structure, which includes three components: -the procedure call stack, the heap, and the function definitions. The -`step` function updates the state by executing a little bit of the -program. The `step` function is called repeatedly to execute the -entire program. - -An implementation of the language (such as a compiler) must be -observationally equivalent to this abstract machine. The notion of -observation is different for each language, and can include things -like input and output. This language is currently so simple that the -only thing that is observable is the final result, an integer. So an -implementation must produce the same final result as the one produces -by the abstract machine. In particular, an implementation does **not** -have to mimic each step of the abstract machine and does not have to -use the same kinds of data structures to store the state of the -program. - -A procedure call frame, defined by the `Frame` structure, includes a -pointer to the function being called, the environment that maps -variables to their addresses, and a to-do list of actions. Each -action corresponds to an expression or statement in the program. The -`Act` structure represents an action. An action often spawns other -actions that needs to be completed first and afterwards uses their -results to complete its action. To keep track of this process, each -action includes a position field `pos` that stores an integer that -starts at `-1` and increments as the action makes progress. For -example, suppose the action associated with an addition expression -`e1 + e2` as at the top of the to-do list: +The dynamic (run-time) semantics is specified by an abstract machine. Abstract +machines have several positive characteristics that make them good for +specification: + +- abstract machines operate on the AST of the program (and not some + lower-level representation such as bytecode) so they directly connect the + program to its behavior + +- abstract machines can easily handle language features with complex + control-flow, such as goto, exceptions, coroutines, and even first-class + continuations. + +The one down-side of abstract machines is that they are not as simple as a +definitional interpreter (a recursive function that interprets the program), but +it is more difficult to handle complex control flow in a definitional +interpreter. + +- [`interp.h`](./interp.h) declares the `interp_program` function. +- [`interp.cc`](./interp.cc) implements `interp_program` function using an + abstract machine, as described below. + +The abstract machine implements a state-transition system. The state is defined +by the `State` structure, which includes three components: the procedure call +stack, the heap, and the function definitions. The `step` function updates the +state by executing a little bit of the program. The `step` function is called +repeatedly to execute the entire program. + +An implementation of the language (such as a compiler) must be observationally +equivalent to this abstract machine. The notion of observation is different for +each language, and can include things like input and output. This language is +currently so simple that the only thing that is observable is the final result, +an integer. So an implementation must produce the same final result as the one +produces by the abstract machine. In particular, an implementation does **not** +have to mimic each step of the abstract machine and does not have to use the +same kinds of data structures to store the state of the program. + +A procedure call frame, defined by the `Frame` structure, includes a pointer to +the function being called, the environment that maps variables to their +addresses, and a to-do list of actions. Each action corresponds to an expression +or statement in the program. The `Act` structure represents an action. An action +often spawns other actions that needs to be completed first and afterwards uses +their results to complete its action. To keep track of this process, each action +includes a position field `pos` that stores an integer that starts at `-1` and +increments as the action makes progress. For example, suppose the action +associated with an addition expression `e1 + e2` as at the top of the to-do +list: (e1 + e2) [-1] :: ... -When this action kicks off (in the `step_exp` function), it increments -`pos` to `0` and pushes `e1` onto the to-do list, so the top of the -todo list now looks like: +When this action kicks off (in the `step_exp` function), it increments `pos` to +`0` and pushes `e1` onto the to-do list, so the top of the todo list now looks +like: e1 [-1] :: (e1 + e2) [0] :: ... -Skipping over the processing of `e1`, it eventually turns into -an integer value `n1`: +Skipping over the processing of `e1`, it eventually turns into an integer value +`n1`: n1 :: (e1 + e2) [0] -Because there is a value at the top of the to-do list, the `step` -function invokes `handle_value` which then dispatches on the next -action on the to-do list, in this case the addition. The addition -action spawns an action for subexpression `e2`, increments -`pos` to `1`, and remembers `n1`. +Because there is a value at the top of the to-do list, the `step` function +invokes `handle_value` which then dispatches on the next action on the to-do +list, in this case the addition. The addition action spawns an action for +subexpression `e2`, increments `pos` to `1`, and remembers `n1`. e2 [-1] :: (e1 + e2) [1](n1) :: ... -Skipping over the processing of `e2`, it eventually turns into -an integer value `n2`: +Skipping over the processing of `e2`, it eventually turns into an integer value +`n2`: n2 :: (e1 + e2) [1](n1) :: ... -Again the `step` function invokes `handle_value` and dispatches to the -addition action which performs the arithmetic and pushes the result on -the to-do list. Let `n3` be the sum of `n1` and `n2`. +Again the `step` function invokes `handle_value` and dispatches to the addition +action which performs the arithmetic and pushes the result on the to-do list. +Let `n3` be the sum of `n1` and `n2`. n3 :: ... -The heap is an array of values. It is used not only for `malloc` but -also to store anything that is mutable, including function parameters -and local variables. A pointer is simply an index into the array. -The `malloc` expression causes the heap to grow (at the end) and -returns the index of the last slot. The dereference expression -returns the nth value of the heap, as specified by the dereferenced -pointer. The assignment operation stores the value of the right-hand -side into the heap at the index specified by the left-hand side +The heap is an array of values. It is used not only for `malloc` but also to +store anything that is mutable, including function parameters and local +variables. A pointer is simply an index into the array. The `malloc` expression +causes the heap to grow (at the end) and returns the index of the last slot. The +dereference expression returns the nth value of the heap, as specified by the +dereferenced pointer. The assignment operation stores the value of the +right-hand side into the heap at the index specified by the left-hand side lvalue. -As you might expect, function calls push a new frame on the stack and -the `return` statement pops a frame off the stack. The parameter -passing semantics is call-by-value, so the machine applies `copy_val` -to the incoming arguments and the outgoing return value. Also, the -machine is careful to kill the parameters and local variables when the -function call is complete. +As you might expect, function calls push a new frame on the stack and the +`return` statement pops a frame off the stack. The parameter passing semantics +is call-by-value, so the machine applies `copy_val` to the incoming arguments +and the outgoing return value. Also, the machine is careful to kill the +parameters and local variables when the function call is complete. The [`examples/`](./examples/) subdirectory includes some example programs. - - -## To-do List - -* scoping of function parameters wrt. other parameters -* generics with interfaces -* nested exhaustiveness checking -* multi-pattern functions - unless return type is empty tuple, in which case, insert - a return of an empty tuple. Or introduce `void`? -* function overloading -* pretty printing with indentation From 49949e85ac706d5ad715c390459342bdcefe600f Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Sun, 25 Oct 2020 09:23:31 -0400 Subject: [PATCH 49/56] added a paragraph about tuples and tuple types --- proposals/p0162.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/proposals/p0162.md b/proposals/p0162.md index 2282a521e5283..ecd202ffbee3b 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -469,3 +469,11 @@ introduce ambiguity. The `pattern` non-terminal is defined in terms of `expression`. It could instead be the other way around, defining `expression` in terms of `pattern`. + +Regarding tuples and tuple types, this proposal follows +[#111](https://github.com/carbon-language/carbon-lang/pull/111) in that there is +not a distinct syntax for tuple types. Instead, the intent is that when a tuple +of types results from an expression in a context that expects a type, the type +system will convert the tuple to a tuple type. An alternative approach has been +discussed in which tuple types have a distinct syntax, such as +`Tuple(Int,Bool)`. From 7292650e580e6bfc402ee6a80018d517cd708c1d Mon Sep 17 00:00:00 2001 From: "Jeremy G. Siek" Date: Wed, 28 Oct 2020 10:57:50 -0400 Subject: [PATCH 50/56] Update proposals/p0162.md Co-authored-by: josh11b --- proposals/p0162.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index ecd202ffbee3b..5782a3c6131a9 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -476,4 +476,4 @@ not a distinct syntax for tuple types. Instead, the intent is that when a tuple of types results from an expression in a context that expects a type, the type system will convert the tuple to a tuple type. An alternative approach has been discussed in which tuple types have a distinct syntax, such as -`Tuple(Int,Bool)`. +`Tuple(Int, Bool)`. From cb7efb69eb7c727d9748e464eff639b146503c51 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 28 Oct 2020 10:59:56 -0400 Subject: [PATCH 51/56] addressing comments from josh11b --- proposals/p0162.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index ecd202ffbee3b..a6c4a6f204c8c 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -98,7 +98,7 @@ abstract syntax definitions in the `executable_semantics` directory. ### Expressions and Patterns The following grammar defines the concrete syntax for expressions. Below we -comment on a few unusual aspects of the grammar. +comment on a few aspects of the grammar. ```Bison pattern: @@ -158,6 +158,23 @@ right-hand side of the above grammar rule, the `expression` to the left of the The grammar rule +```Bison +expression: "fnty" tuple return_type +``` + +is for function types. They are meant to play the role that function pointers +play in C and that `std::function` plays in C++. + +Regarding the grammar rule for the return type of a function: + +```Bison +return_type: "->" expression +``` + +the `expression` is expected to evaluate to a type at compile-time. + +The grammar rule + ```Bison tuple: '(' field_list ')' ``` @@ -248,10 +265,9 @@ In the grammar rule for function definitions declaration: "fn" identifier tuple return_type '{' statement_list '}' ``` -the `tuple` is used as a pattern to describe the parameters of the function -whereas the `expression` in the `return_type` must evaluate to a type at -compile-time. The grammar for function definitions does not currently include -implicit parameters, but the intent is to add them to the grammar in the future. +the `tuple` is used as a pattern to describe the parameters of the function. The +grammar for function definitions does not currently include implicit parameters, +but the intent is to add them to the grammar in the future. In the rule for field declarations From fa8a24689e8d741c76ec22f0f4b81b632d6832df Mon Sep 17 00:00:00 2001 From: "Jeremy G. Siek" Date: Wed, 28 Oct 2020 13:17:25 -0400 Subject: [PATCH 52/56] Update executable-semantics/README.md Co-authored-by: Dmitri Gribenko --- executable-semantics/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/executable-semantics/README.md b/executable-semantics/README.md index afae9c0390838..11d81056056d1 100644 --- a/executable-semantics/README.md +++ b/executable-semantics/README.md @@ -81,7 +81,7 @@ often spawns other actions that needs to be completed first and afterwards uses their results to complete its action. To keep track of this process, each action includes a position field `pos` that stores an integer that starts at `-1` and increments as the action makes progress. For example, suppose the action -associated with an addition expression `e1 + e2` as at the top of the to-do +associated with an addition expression `e1 + e2` is at the top of the to-do list: (e1 + e2) [-1] :: ... From 6462c6f5a7f259b8939b5c0ea42a1ec7c5a80ddd Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Wed, 28 Oct 2020 13:21:39 -0400 Subject: [PATCH 53/56] link to implicit parameters in generics proposal --- proposals/p0162.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/proposals/p0162.md b/proposals/p0162.md index 5789ecb0bd8f2..ad5c9e50586a9 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -266,7 +266,8 @@ declaration: "fn" identifier tuple return_type '{' statement_list '}' ``` the `tuple` is used as a pattern to describe the parameters of the function. The -grammar for function definitions does not currently include implicit parameters, +grammar for function definitions does not currently include +[implicit parameters](https://github.com/josh11b/carbon-lang/blob/generics-docs/docs/design/generics/terminology.md#implicit-parameter), but the intent is to add them to the grammar in the future. In the rule for field declarations From 535d46f3df0753765b04aed9b519b503b55e424d Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 29 Oct 2020 09:18:44 -0400 Subject: [PATCH 54/56] added non-terminal for designator as per geoffromer's suggestion --- executable-semantics/syntax.y | 27 +++++++++++++++------------ proposals/p0162.md | 20 ++++++++++++-------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/executable-semantics/syntax.y b/executable-semantics/syntax.y index b040e91b8873e..413869be6b43f 100644 --- a/executable-semantics/syntax.y +++ b/executable-semantics/syntax.y @@ -30,7 +30,7 @@ using std::pair; using std::make_pair; using std::cout; using std::endl; - + static list program; %} %union @@ -56,6 +56,7 @@ static list program; %token integer_literal %token identifier +%type designator %type declaration %type function_declaration %type function_definition @@ -104,13 +105,13 @@ static list program; %token DBLARROW %token DEFAULT %token AUTO -%nonassoc '{' '}' +%nonassoc '{' '}' %nonassoc ':' ',' DBLARROW %left OR AND %nonassoc EQUAL NOT %left '+' '-' -%left '.' ARROW -%nonassoc '(' ')' '[' ']' +%left '.' ARROW +%nonassoc '(' ')' '[' ']' %start input %locations %% @@ -142,8 +143,8 @@ pattern: expression: identifier { $$ = make_var(yylineno, $1); } -| expression '.' identifier - { $$ = make_get_field(yylineno, $1, $3); } +| expression designator + { $$ = make_get_field(yylineno, $1, $2); } | expression '[' expression ']' { $$ = make_index(yylineno, $1, $3); } | expression ':' identifier @@ -181,6 +182,8 @@ expression: | FNTY tuple return_type { $$ = make_fun_type(yylineno, $2, $3); } ; +designator: '.' identifier { $$ = $2; } +; tuple: '(' field_list ')' { switch ($2->tag) { @@ -198,9 +201,9 @@ tuple: '(' field_list ')' field: pattern { $$ = make_exp($1); } -| '.' identifier '=' pattern +| designator '=' pattern { auto fields = new list >(); - fields->push_back(make_pair($2, $4)); + fields->push_back(make_pair($1, $3)); $$ = make_field_list(fields); } ; field_list: @@ -214,8 +217,8 @@ field_list: clause: CASE pattern DBLARROW statement { $$ = new pair($2, $4); } -| DEFAULT DBLARROW statement - { +| DEFAULT DBLARROW statement + { auto vp = make_var_pat(yylineno, "_", make_auto_type(yylineno)); $$ = new pair(vp, $3); } @@ -308,7 +311,7 @@ declaration_list: { $$ = $2; $$->push_front($1); } ; %% -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) { /*yydebug = 1;*/ if (argc > 1) { @@ -319,6 +322,6 @@ int main(int argc, char* argv[]) { FILE* program = fopen(argv[2], "r"); input = read_file(program); } - yyparse(); + yyparse(); return 0; } diff --git a/proposals/p0162.md b/proposals/p0162.md index ad5c9e50586a9..9dd4e4dc0a7d9 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -106,7 +106,7 @@ pattern: ; expression: identifier -| expression '.' identifier +| expression designator | expression '[' expression ']' | expression ':' identifier | integer_literal @@ -124,6 +124,9 @@ expression: | "auto" | "fnty" tuple return_type ; +designator: + '.' identifier +; tuple: '(' field_list ')' ; @@ -134,7 +137,7 @@ field_list: ; field: pattern -| '.' identifier '=' pattern +| designator '=' pattern ; return_type: /* empty */ @@ -183,18 +186,19 @@ is primarily for constructing a tuple, but it is also used for creating tuple types and tuple patterns, depending on the context in which the expression occurs. -Regarding the grammar rule for a named argument +Regarding the grammar rules for `designator` and for a named argument ```Bison -field: '.' identifier '=' pattern +designator: '.' identifier +field: designator '=' pattern ``` -the period is needed to aid integrated development environments with +The period enables the addition of new keywords to Carbon without colliding with +field names. The period also aids integrated development environments with auto-completion. The issue is that without the period, when the programmer is typing the identifier (and not yet typed the `=`), the IDE doesn't know whether the identifier is for a positional argument, in which case it is a variable -occurrence, or whether the identifier is a field name. The period also enables -the addition of new keywords to Carbon without colliding with field names. +occurrence, or whether the identifier is a field name. ### Statements @@ -339,7 +343,7 @@ straightforward. However, the parsing of the `field_list` deserves some explanation. The fields can be labeled with the grammar rule: ```Bison -field: '.' identifier '=' pattern +field: designator '=' pattern ``` or unlabeled, with the rule From 1036becbb17699a64dfc10619fbe35589b353a05 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Thu, 29 Oct 2020 09:58:36 -0400 Subject: [PATCH 55/56] changed handling of tuples in function call and similar places as per josh11b and zygoloid --- executable-semantics/ast.cc | 24 +++++++++++++------ executable-semantics/ast.h | 12 +++++----- .../examples/fun6-fail-type.6c | 7 ++++++ executable-semantics/syntax.y | 11 ++++++++- proposals/p0162.md | 16 +++++++++++++ 5 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 executable-semantics/examples/fun6-fail-type.6c diff --git a/executable-semantics/ast.cc b/executable-semantics/ast.cc index ffe906cd372bc..ca708987fab46 100644 --- a/executable-semantics/ast.cc +++ b/executable-semantics/ast.cc @@ -224,7 +224,7 @@ void print_exp(Expression* e) { cout << e->u.integer; break; case Boolean: - cout << std::boolalpha; + cout << std::boolalpha; cout << e->u.boolean; break; case PrimitiveOp: @@ -327,6 +327,16 @@ ExpOrFieldList* cons_field(ExpOrFieldList* e1, ExpOrFieldList* e2) { return make_field_list(fields); } +Expression* ensure_tuple(int lineno, Expression* e) { + if (e->tag == Tuple) { + return e; + } else { + auto vec = new vector >(); + vec->push_back(make_pair("", e)); + return make_tuple(lineno, vec); + } +} + /***** Statements *****/ Statement* make_exp_stmt(int lineno, Expression* exp) { @@ -355,13 +365,13 @@ Statement* make_var_def(int lineno, Expression* pat, Expression* init) { return s; } -Statement* make_if(int lineno, Expression* cond, Statement* thn, Statement* els) { +Statement* make_if(int lineno, Expression* cond, Statement* then_, Statement* else_) { Statement* s = new Statement(); s->lineno = lineno; s->tag = If; s->u.if_stmt.cond = cond; - s->u.if_stmt.thn = thn; - s->u.if_stmt.els = els; + s->u.if_stmt.then_ = then_; + s->u.if_stmt.else_ = else_; return s; } @@ -483,9 +493,9 @@ void print_stmt(Statement* s, int depth) { cout << "if ("; print_exp(s->u.if_stmt.cond); cout << ")" << endl; - print_stmt(s->u.if_stmt.thn, depth - 1); + print_stmt(s->u.if_stmt.then_, depth - 1); cout << endl << "else" << endl; - print_stmt(s->u.if_stmt.els, depth - 1); + print_stmt(s->u.if_stmt.else_, depth - 1); break; case Return: cout << "return "; @@ -494,7 +504,7 @@ void print_stmt(Statement* s, int depth) { break; case Sequence: print_stmt(s->u.sequence.stmt, depth); - if (depth < 0 || depth > 1) + if (depth < 0 || depth > 1) cout << endl; print_stmt(s->u.sequence.next, depth - 1); break; diff --git a/executable-semantics/ast.h b/executable-semantics/ast.h index fec2a33e69089..b54158e05f3b9 100644 --- a/executable-semantics/ast.h +++ b/executable-semantics/ast.h @@ -50,7 +50,7 @@ typedef list< pair > VarTypes; /***** Expressions *****/ enum ExpressionKind { Variable, PatternVariable, Integer, Boolean, - PrimitiveOp, Call, Tuple, Index, GetField, + PrimitiveOp, Call, Tuple, Index, GetField, IntT, BoolT, TypeT, FunctionT, AutoT }; enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; @@ -113,7 +113,7 @@ ExpOrFieldList* cons_field(ExpOrFieldList* e1, ExpOrFieldList* e2); /***** Statements *****/ -enum StatementKind { ExpressionStatement, Assign, VariableDefinition, +enum StatementKind { ExpressionStatement, Assign, VariableDefinition, If, Return, Sequence, Block, While, Break, Continue, Match }; @@ -124,7 +124,7 @@ struct Statement { Expression* exp; struct { Expression* lhs; Expression* rhs; } assign; struct { Expression* pat; Expression* init; } variable_definition; - struct { Expression* cond; Statement* thn; Statement* els; } if_stmt; + struct { Expression* cond; Statement* then_; Statement* else_; } if_stmt; Expression* return_stmt; struct { Statement* stmt; Statement* next; } sequence; struct { Statement* stmt; } block; @@ -139,8 +139,8 @@ struct Statement { Statement* make_exp_stmt(int lineno, Expression* exp); Statement* make_assign(int lineno, Expression* lhs, Expression* rhs); Statement* make_var_def(int lineno, Expression* pat, Expression* init); -Statement* make_if(int lineno, Expression* cond, Statement* thn, - Statement* els); +Statement* make_if(int lineno, Expression* cond, Statement* then_, + Statement* else_); Statement* make_return(int lineno, Expression* e); Statement* make_seq(int lineno, Statement* s1, Statement* s2); Statement* make_block(int lineno, Statement* s); @@ -181,7 +181,7 @@ Member* make_field(int lineno, string name, Expression* type); struct StructDefinition { int lineno; string* name; - list* members; + list* members; }; enum DeclarationKind { FunctionDeclaration, StructDeclaration, diff --git a/executable-semantics/examples/fun6-fail-type.6c b/executable-semantics/examples/fun6-fail-type.6c new file mode 100644 index 0000000000000..000af41653cd5 --- /dev/null +++ b/executable-semantics/examples/fun6-fail-type.6c @@ -0,0 +1,7 @@ +fn f(Int: x, Int: y) -> Int { return x + y; } + +fn main() -> Int { + var (Int, Int): xy = (1, 2); + // should fail to type-check + return f(xy); +} \ No newline at end of file diff --git a/executable-semantics/syntax.y b/executable-semantics/syntax.y index 413869be6b43f..7a1a5f4ac302e 100644 --- a/executable-semantics/syntax.y +++ b/executable-semantics/syntax.y @@ -178,7 +178,16 @@ expression: { $$ = make_unop(yylineno, Not, $2); } | '-' expression { $$ = make_unop(yylineno, Neg, $2); } -| expression tuple { $$ = make_call(yylineno, $1, $2); } +| expression tuple + { + if ($2->tag == Tuple) { + $$ = make_call(yylineno, $1, $2); + } else { + auto vec = new vector >(); + vec->push_back(make_pair("", $2)); + $$ = make_call(yylineno, $1, make_tuple(yylineno, vec)); + } + } | FNTY tuple return_type { $$ = make_fun_type(yylineno, $2, $3); } ; diff --git a/proposals/p0162.md b/proposals/p0162.md index 9dd4e4dc0a7d9..8ddb9d6839732 100644 --- a/proposals/p0162.md +++ b/proposals/p0162.md @@ -365,6 +365,22 @@ if the field list only has a single unlabeled item without a trailing comma, then the parse result for that item is returned. Otherwise a `tuple` AST node is created containing the parse results for the fields. +In the following grammar rules, if the parse result for `tuple` is not +a tuple (because the field list was a single unlabeled item without a +trailing comma), then a tuple is wrapped around the expression to +ensure that it is a tuple. + +```Bison +expression: expression tuple +expression: "fnty" tuple return_type +function_definition: + "fn" identifier tuple return_type '{' statement_list '}' +| "fn" identifier tuple DBLARROW expression ';' +| "fn" identifier tuple return_type ';' +alternative: identifier tuple ';' +``` + + #### Abstract Syntax for Statements ```C++ From 2a81ff436bfb19e8d4b42d5eb54a704f25626660 Mon Sep 17 00:00:00 2001 From: Jeremy Siek Date: Fri, 30 Oct 2020 09:19:40 -0400 Subject: [PATCH 56/56] moving code to separate PR --- executable-semantics/Makefile | 51 - executable-semantics/README.md | 133 -- executable-semantics/assoc_list.h | 33 - executable-semantics/ast.cc | 663 ------- executable-semantics/ast.h | 238 --- executable-semantics/cons_list.h | 26 - executable-semantics/examples/block1.6c | 7 - executable-semantics/examples/break1.6c | 10 - executable-semantics/examples/choice1.6c | 28 - executable-semantics/examples/continue1.6c | 9 - executable-semantics/examples/fun-recur.6c | 10 - executable-semantics/examples/fun1.6c | 7 - executable-semantics/examples/fun2.6c | 14 - executable-semantics/examples/fun3.6c | 8 - executable-semantics/examples/fun4.6c | 7 - executable-semantics/examples/fun5.6c | 5 - .../examples/fun6-fail-type.6c | 7 - executable-semantics/examples/funptr1.6c | 8 - .../examples/match-int-default.6c | 11 - executable-semantics/examples/match-int.6c | 9 - executable-semantics/examples/match-type.6c | 17 - executable-semantics/examples/next.6c | 5 - executable-semantics/examples/pattern-init.6c | 4 - executable-semantics/examples/record1.6c | 5 - executable-semantics/examples/struct1.6c | 9 - executable-semantics/examples/struct2.6c | 11 - executable-semantics/examples/struct3.6c | 8 - executable-semantics/examples/tuple-assign.6c | 6 - executable-semantics/examples/tuple-match.6c | 7 - executable-semantics/examples/tuple1.6c | 6 - executable-semantics/examples/tuple2.6c | 4 - executable-semantics/examples/undef1.6c | 14 - executable-semantics/examples/undef2.6c | 9 - executable-semantics/examples/while1.6c | 6 - executable-semantics/examples/zero.6c | 3 - executable-semantics/interp.cc | 1704 ----------------- executable-semantics/interp.h | 131 -- executable-semantics/syntax.l | 77 - executable-semantics/syntax.y | 336 ---- executable-semantics/typecheck.cc | 862 --------- executable-semantics/typecheck.h | 50 - 41 files changed, 4558 deletions(-) delete mode 100644 executable-semantics/Makefile delete mode 100644 executable-semantics/README.md delete mode 100644 executable-semantics/assoc_list.h delete mode 100644 executable-semantics/ast.cc delete mode 100644 executable-semantics/ast.h delete mode 100644 executable-semantics/cons_list.h delete mode 100644 executable-semantics/examples/block1.6c delete mode 100644 executable-semantics/examples/break1.6c delete mode 100644 executable-semantics/examples/choice1.6c delete mode 100644 executable-semantics/examples/continue1.6c delete mode 100644 executable-semantics/examples/fun-recur.6c delete mode 100644 executable-semantics/examples/fun1.6c delete mode 100644 executable-semantics/examples/fun2.6c delete mode 100644 executable-semantics/examples/fun3.6c delete mode 100644 executable-semantics/examples/fun4.6c delete mode 100644 executable-semantics/examples/fun5.6c delete mode 100644 executable-semantics/examples/fun6-fail-type.6c delete mode 100644 executable-semantics/examples/funptr1.6c delete mode 100644 executable-semantics/examples/match-int-default.6c delete mode 100644 executable-semantics/examples/match-int.6c delete mode 100644 executable-semantics/examples/match-type.6c delete mode 100644 executable-semantics/examples/next.6c delete mode 100644 executable-semantics/examples/pattern-init.6c delete mode 100644 executable-semantics/examples/record1.6c delete mode 100644 executable-semantics/examples/struct1.6c delete mode 100644 executable-semantics/examples/struct2.6c delete mode 100644 executable-semantics/examples/struct3.6c delete mode 100644 executable-semantics/examples/tuple-assign.6c delete mode 100644 executable-semantics/examples/tuple-match.6c delete mode 100644 executable-semantics/examples/tuple1.6c delete mode 100644 executable-semantics/examples/tuple2.6c delete mode 100644 executable-semantics/examples/undef1.6c delete mode 100644 executable-semantics/examples/undef2.6c delete mode 100644 executable-semantics/examples/while1.6c delete mode 100644 executable-semantics/examples/zero.6c delete mode 100644 executable-semantics/interp.cc delete mode 100644 executable-semantics/interp.h delete mode 100644 executable-semantics/syntax.l delete mode 100644 executable-semantics/syntax.y delete mode 100644 executable-semantics/typecheck.cc delete mode 100644 executable-semantics/typecheck.h diff --git a/executable-semantics/Makefile b/executable-semantics/Makefile deleted file mode 100644 index e4e71b85f0b7a..0000000000000 --- a/executable-semantics/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -a.out: syntax.l syntax.y ast.h typecheck.h interp.h ast.cc typecheck.cc interp.cc cons_list.h assoc_list.h - bison -d syntax.y - flex syntax.l - bison -v --debug syntax.y -o syntax.tab.cc - g++ -std=c++11 -Wno-deprecated-register -c -g lex.yy.c - g++ -std=c++11 -c -g ast.cc - g++ -std=c++11 -c -g typecheck.cc - g++ -std=c++11 -c -g interp.cc - g++ -std=c++11 -g lex.yy.o ast.o typecheck.o interp.o syntax.tab.cc - -# TODO: replace this with a script that can handle -# tests that are suppose to produce errors. -test: - ./a.out examples/zero.6c - ./a.out examples/next.6c - ./a.out examples/while1.6c - ./a.out examples/fun1.6c - ./a.out examples/fun2.6c - ./a.out examples/fun3.6c - ./a.out examples/fun4.6c - ./a.out examples/fun5.6c - ./a.out examples/fun-recur.6c - ./a.out examples/funptr1.6c - ./a.out examples/block1.6c - ./a.out examples/break1.6c - ./a.out examples/continue1.6c - ./a.out examples/struct1.6c - ./a.out examples/struct2.6c - ./a.out examples/choice1.6c - ./a.out examples/tuple1.6c - ./a.out examples/tuple2.6c - ./a.out examples/tuple-match.6c - ./a.out examples/tuple-assign.6c - ./a.out examples/record1.6c - ./a.out examples/match-int.6c - ./a.out examples/match-int-default.6c - ./a.out examples/match-type.6c - ./a.out examples/pattern-init.6c - ./a.out examples/struct3.6c - -clean: - rm -f a.out - rm -f *.o - rm -f syntax.tab.h - rm -f syntax.tab.cc syntax.tab.c syntax.output - rm -f lex.yy.c lex.yy.cc - rm -f a.out - rm -rf a.out.dSYM - rm -f log - rm -f *~ - rm -f examples/*~ diff --git a/executable-semantics/README.md b/executable-semantics/README.md deleted file mode 100644 index 11d81056056d1..0000000000000 --- a/executable-semantics/README.md +++ /dev/null @@ -1,133 +0,0 @@ -# Executable Semantics - -This directory contains a work-in-progress executable semantics. It started as -an executable semantics for Featherweight C and it is migrating into an -executable semantics for the Carbon language. It includes a parser, type -checker, and abstract machine. - -This language currently includes several kinds of values: integer, booleans, -functions, and structs. A kind of safe union, called a `choice`, is in -progress. Regarding control-flow, it includes if statements, while loops, break, -continue, function calls, and a variant of `switch` called `match` is in -progress. - -The grammar of the language matches the one in Proposal -[#162](https://github.com/carbon-language/carbon-lang/pull/162). The -type checker and abstract machine do not yet have a corresponding -proposal. Nevertheless they are present here to help test the parser -but should not be considered definitive. - -The parser is implemented using the flex and bison parser generator tools. - -- [`syntax.l`](./syntax.l) the lexer specification -- [`syntax.y`](./syntax.y) the grammar - -The parser translates program text into an abstract syntax tree (AST). - -- [`ast.h`](./ast.h) includes structure definitions for the AST and function - declarations for creating and printing ASTs. -- [`ast.cc`](./ast.cc) contains the function definitions. - -The type checker defines what it means for an AST to be a valid program. The -type checker prints an error and exits if the AST is invalid. - -- [`typecheck.h`](./typecheck.h) -- [`typecheck.cc`](./typecheck.cc) - -The parser and type checker together specify the static (compile-time) -semantics. - -The dynamic (run-time) semantics is specified by an abstract machine. Abstract -machines have several positive characteristics that make them good for -specification: - -- abstract machines operate on the AST of the program (and not some - lower-level representation such as bytecode) so they directly connect the - program to its behavior - -- abstract machines can easily handle language features with complex - control-flow, such as goto, exceptions, coroutines, and even first-class - continuations. - -The one down-side of abstract machines is that they are not as simple as a -definitional interpreter (a recursive function that interprets the program), but -it is more difficult to handle complex control flow in a definitional -interpreter. - -- [`interp.h`](./interp.h) declares the `interp_program` function. -- [`interp.cc`](./interp.cc) implements `interp_program` function using an - abstract machine, as described below. - -The abstract machine implements a state-transition system. The state is defined -by the `State` structure, which includes three components: the procedure call -stack, the heap, and the function definitions. The `step` function updates the -state by executing a little bit of the program. The `step` function is called -repeatedly to execute the entire program. - -An implementation of the language (such as a compiler) must be observationally -equivalent to this abstract machine. The notion of observation is different for -each language, and can include things like input and output. This language is -currently so simple that the only thing that is observable is the final result, -an integer. So an implementation must produce the same final result as the one -produces by the abstract machine. In particular, an implementation does **not** -have to mimic each step of the abstract machine and does not have to use the -same kinds of data structures to store the state of the program. - -A procedure call frame, defined by the `Frame` structure, includes a pointer to -the function being called, the environment that maps variables to their -addresses, and a to-do list of actions. Each action corresponds to an expression -or statement in the program. The `Act` structure represents an action. An action -often spawns other actions that needs to be completed first and afterwards uses -their results to complete its action. To keep track of this process, each action -includes a position field `pos` that stores an integer that starts at `-1` and -increments as the action makes progress. For example, suppose the action -associated with an addition expression `e1 + e2` is at the top of the to-do -list: - - (e1 + e2) [-1] :: ... - -When this action kicks off (in the `step_exp` function), it increments `pos` to -`0` and pushes `e1` onto the to-do list, so the top of the todo list now looks -like: - - e1 [-1] :: (e1 + e2) [0] :: ... - -Skipping over the processing of `e1`, it eventually turns into an integer value -`n1`: - - n1 :: (e1 + e2) [0] - -Because there is a value at the top of the to-do list, the `step` function -invokes `handle_value` which then dispatches on the next action on the to-do -list, in this case the addition. The addition action spawns an action for -subexpression `e2`, increments `pos` to `1`, and remembers `n1`. - - e2 [-1] :: (e1 + e2) [1](n1) :: ... - -Skipping over the processing of `e2`, it eventually turns into an integer value -`n2`: - - n2 :: (e1 + e2) [1](n1) :: ... - -Again the `step` function invokes `handle_value` and dispatches to the addition -action which performs the arithmetic and pushes the result on the to-do list. -Let `n3` be the sum of `n1` and `n2`. - - n3 :: ... - -The heap is an array of values. It is used not only for `malloc` but also to -store anything that is mutable, including function parameters and local -variables. A pointer is simply an index into the array. The `malloc` expression -causes the heap to grow (at the end) and returns the index of the last slot. The -dereference expression returns the nth value of the heap, as specified by the -dereferenced pointer. The assignment operation stores the value of the -right-hand side into the heap at the index specified by the left-hand side -lvalue. - -As you might expect, function calls push a new frame on the stack and the -`return` statement pops a frame off the stack. The parameter passing semantics -is call-by-value, so the machine applies `copy_val` to the incoming arguments -and the outgoing return value. Also, the machine is careful to kill the -parameters and local variables when the function call is complete. - -The [`examples/`](./examples/) subdirectory includes some example programs. diff --git a/executable-semantics/assoc_list.h b/executable-semantics/assoc_list.h deleted file mode 100644 index f21ad0855a94f..0000000000000 --- a/executable-semantics/assoc_list.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ASSOC_LIST_H -#define ASSOC_LIST_H - -#include -#include -using std::cerr; -using std::endl; - -template -struct AList { - K key; - V value; - AList* next; - AList(K k, V v, AList* n) : key(k), value(v), next(n) { } -}; - -template -V lookup(int lineno, AList* alist, K key, void (*print_key)(K)) { - if (alist == NULL) { - cerr << lineno << ": could not find `"; - print_key(key); - cerr << "`" << endl; - exit(-1); - } else if (alist->key == key) { - return alist->value; - } else { - return lookup(lineno, alist->next, key, print_key); - } -} - - - -#endif diff --git a/executable-semantics/ast.cc b/executable-semantics/ast.cc deleted file mode 100644 index ca708987fab46..0000000000000 --- a/executable-semantics/ast.cc +++ /dev/null @@ -1,663 +0,0 @@ -#include -#include -#include -#include -#include "ast.h" -#include "interp.h" - -using std::cout; -using std::endl; -using std::make_pair; - -/***** Utilities *****/ - -char* input; - -/***** Types *****/ - -Expression* make_type_type(int lineno) { - Expression* t = new Expression(); - t->tag = TypeT; - t->lineno = lineno; - return t; -} - -Expression* make_int_type(int lineno) { - Expression* t = new Expression(); - t->tag = IntT; - t->lineno = lineno; - return t; -} - -Expression* make_bool_type(int lineno) { - Expression* t = new Expression(); - t->tag = BoolT; - t->lineno = lineno; - return t; -} - -Expression* make_auto_type(int lineno) { - Expression* t = new Expression(); - t->tag = AutoT; - t->lineno = lineno; - return t; -} - -Expression* make_fun_type(int lineno, Expression* param, Expression* ret) { - Expression* t = new Expression(); - t->tag = FunctionT; - t->lineno = lineno; - t->u.function_type.parameter = param; - t->u.function_type.return_type = ret; - return t; -} - -void print_string(string* s) { - cout << *s; -} - -/***** Expressions *****/ - -Expression* make_var(int lineno, string var) { - Expression* v = new Expression(); - v->lineno = lineno; - v->tag = Variable; - v->u.variable.name = new string(var); - return v; -} - -Expression* make_var_pat(int lineno, string var, Expression* type) { - Expression* v = new Expression(); - v->lineno = lineno; - v->tag = PatternVariable; - v->u.pattern_variable.name = new string(var); - v->u.pattern_variable.type = type; - return v; -} - -Expression* make_int(int lineno, int i) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = Integer; - e->u.integer = i; - return e; -} - -Expression* make_bool(int lineno, bool b) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = Boolean; - e->u.boolean = b; - return e; -} - -Expression* make_op(int lineno, enum Operator op, vector* args) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = PrimitiveOp; - e->u.primitive_op.operator_ = op; - e->u.primitive_op.arguments = args; - return e; -} - -Expression* make_unop(int lineno, enum Operator op, Expression* arg) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = PrimitiveOp; - e->u.primitive_op.operator_ = op; - vector* args = new vector(); - args->push_back(arg); - e->u.primitive_op.arguments = args; - return e; -} - -Expression* make_binop(int lineno, enum Operator op, Expression* arg1, Expression* arg2) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = PrimitiveOp; - e->u.primitive_op.operator_ = op; - vector* args = new vector(); - args->push_back(arg1); - args->push_back(arg2); - e->u.primitive_op.arguments = args; - return e; -} - -Expression* make_call(int lineno, Expression* fun, Expression* arg) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = Call; - e->u.call.function = fun; - e->u.call.argument = arg; - return e; -} - -Expression* make_get_field(int lineno, Expression* exp, string field) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = GetField; - e->u.get_field.aggregate = exp; - e->u.get_field.field = new string(field); - return e; -} - -Expression* make_tuple(int lineno, vector >* args) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = Tuple; - int i = 0; - for (auto f = args->begin(); f != args->end(); ++f) { - if (f->first == "") { - f->first = std::to_string(i); - ++i; - } - } - e->u.tuple.fields = args; - return e; -} - -Expression* make_index(int lineno, Expression* exp, Expression* i) { - Expression* e = new Expression(); - e->lineno = lineno; - e->tag = Index; - e->u.index.aggregate = exp; - e->u.index.offset = i; - return e; -} - -void print_op(Operator op) { - switch (op) { - case Neg: - cout << "-"; - break; - case Add: - cout << "+"; - break; - case Sub: - cout << "-"; - break; - case Not: - cout << "!"; - break; - case And: - cout << "&&"; - break; - case Or: - cout << "||"; - break; - case Eq: - cout << "=="; - break; - } -} - -void print_fields(vector >* fields) { - int i = 0; - for (auto iter = fields->begin(); iter != fields->end(); ++iter, ++i) { - if (i != 0) - cout << ", "; - cout << iter->first; - cout << " = "; - print_exp(iter->second); - } -} - -void print_exp(Expression* e) { - switch (e->tag) { - case Index: - print_exp(e->u.index.aggregate); - cout << "["; - print_exp(e->u.index.offset); - cout << "]"; - break; - case GetField: - print_exp(e->u.get_field.aggregate); - cout << "."; - cout << * e->u.get_field.field; - break; - case Tuple: - cout << "("; - print_fields(e->u.tuple.fields); - cout << ")"; - break; - case Integer: - cout << e->u.integer; - break; - case Boolean: - cout << std::boolalpha; - cout << e->u.boolean; - break; - case PrimitiveOp: - cout << "("; - if (e->u.primitive_op.arguments->size() == 0) { - print_op(e->u.primitive_op.operator_); - } else if (e->u.primitive_op.arguments->size() == 1) { - print_op(e->u.primitive_op.operator_); - cout << " "; - auto iter = e->u.primitive_op.arguments->begin(); - print_exp(*iter); - } else if (e->u.primitive_op.arguments->size() == 2) { - auto iter = e->u.primitive_op.arguments->begin(); - print_exp(*iter); - cout << " "; - print_op(e->u.primitive_op.operator_); - cout << " "; - ++iter; - print_exp(*iter); - } - cout << ")"; - break; - case Variable: - cout << * e->u.variable.name; - break; - case PatternVariable: - print_exp(e->u.pattern_variable.type); - cout << ": "; - cout << * e->u.pattern_variable.name; - break; - case Call: - print_exp(e->u.call.function); - if (e->u.call.argument->tag == Tuple) { - print_exp(e->u.call.argument); - } else { - cout << "("; - print_exp(e->u.call.argument); - cout << ")"; - } - break; - case BoolT: - cout << "Bool"; - break; - case IntT: - cout << "Int"; - break; - case TypeT: - cout << "Type"; - break; - case AutoT: - cout << "auto"; - break; - case FunctionT: - cout << "fn "; - print_exp(e->u.function_type.parameter); - cout << " -> "; - print_exp(e->u.function_type.return_type); - break; - } -} - -/***** Expression or Field List *****/ - -ExpOrFieldList* make_exp(Expression* exp) { - auto e = new ExpOrFieldList(); - e->tag = Exp; - e->u.exp = exp; - return e; -} - -ExpOrFieldList* make_field_list(list >* fields) { - auto e = new ExpOrFieldList(); - e->tag = FieldList; - e->u.fields = fields; - return e; -} - -ExpOrFieldList* cons_field(ExpOrFieldList* e1, ExpOrFieldList* e2) { - auto fields = new list >(); - switch (e1->tag) { - case Exp: - fields->push_back(make_pair("", e1->u.exp)); - break; - case FieldList: - for (auto i = e1->u.fields->begin(); i != e1->u.fields->end(); ++i) { - fields->push_back(*i); - } - break; - } - switch (e2->tag) { - case Exp: - fields->push_back(make_pair("", e2->u.exp)); - break; - case FieldList: - for (auto i = e2->u.fields->begin(); i != e2->u.fields->end(); ++i) { - fields->push_back(*i); - } - break; - } - return make_field_list(fields); -} - -Expression* ensure_tuple(int lineno, Expression* e) { - if (e->tag == Tuple) { - return e; - } else { - auto vec = new vector >(); - vec->push_back(make_pair("", e)); - return make_tuple(lineno, vec); - } -} - -/***** Statements *****/ - -Statement* make_exp_stmt(int lineno, Expression* exp) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = ExpressionStatement; - s->u.exp = exp; - return s; -} - -Statement* make_assign(int lineno, Expression* lhs, Expression* rhs) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Assign; - s->u.assign.lhs = lhs; - s->u.assign.rhs = rhs; - return s; -} - -Statement* make_var_def(int lineno, Expression* pat, Expression* init) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = VariableDefinition; - s->u.variable_definition.pat = pat; - s->u.variable_definition.init = init; - return s; -} - -Statement* make_if(int lineno, Expression* cond, Statement* then_, Statement* else_) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = If; - s->u.if_stmt.cond = cond; - s->u.if_stmt.then_ = then_; - s->u.if_stmt.else_ = else_; - return s; -} - -Statement* make_while(int lineno, Expression* cond, Statement* body) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = While; - s->u.while_stmt.cond = cond; - s->u.while_stmt.body = body; - return s; -} - -Statement* make_break(int lineno) { - cout << "make_block" << endl; - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Break; - return s; -} - -Statement* make_continue(int lineno) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Continue; - return s; -} - -Statement* make_return(int lineno, Expression* e) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Return; - s->u.return_stmt = e; - return s; -} - -Statement* make_seq(int lineno, Statement* s1, Statement* s2) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Sequence; - s->u.sequence.stmt = s1; - s->u.sequence.next = s2; - return s; -} - -Statement* make_block(int lineno, Statement* stmt) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Block; - s->u.block.stmt = stmt; - return s; -} - -Statement* make_match(int lineno, Expression* exp, list< pair >* clauses) { - Statement* s = new Statement(); - s->lineno = lineno; - s->tag = Match; - s->u.match_stmt.exp = exp; - s->u.match_stmt.clauses = clauses; - return s; -} - -void print_stmt(Statement* s, int depth) { - if (! s) - return; - if (depth == 0) { - cout << " ... "; - return; - } - switch (s->tag) { - case Match: - cout << "match ("; - print_exp(s->u.match_stmt.exp); - cout << ") {"; - if (depth < 0 || depth > 1) { - cout << endl; - for (auto c = s->u.match_stmt.clauses->begin(); - c != s->u.match_stmt.clauses->end(); ++c) { - cout << "case "; - print_exp(c->first); - cout << " =>" << endl; - print_stmt(c->second, depth - 1); - cout << endl; - } - } else { - cout << "..."; - } - cout << "}"; - break; - case While: - cout << "while ("; - print_exp(s->u.while_stmt.cond); - cout << ")" << endl; - print_stmt(s->u.while_stmt.body, depth - 1); - break; - case Break: - cout << "break;"; - break; - case Continue: - cout << "continue;"; - break; - case VariableDefinition: - cout << "var "; - print_exp(s->u.variable_definition.pat); - cout << " = "; - print_exp(s->u.variable_definition.init); - cout << ";"; - break; - case ExpressionStatement: - print_exp(s->u.exp); - cout << ";"; - break; - case Assign: - print_exp(s->u.assign.lhs); - cout << " = "; - print_exp(s->u.assign.rhs); - cout << ";"; - break; - case If: - cout << "if ("; - print_exp(s->u.if_stmt.cond); - cout << ")" << endl; - print_stmt(s->u.if_stmt.then_, depth - 1); - cout << endl << "else" << endl; - print_stmt(s->u.if_stmt.else_, depth - 1); - break; - case Return: - cout << "return "; - print_exp(s->u.return_stmt); - cout << ";"; - break; - case Sequence: - print_stmt(s->u.sequence.stmt, depth); - if (depth < 0 || depth > 1) - cout << endl; - print_stmt(s->u.sequence.next, depth - 1); - break; - case Block: - cout << "{" << endl; - print_stmt(s->u.block.stmt, depth - 1); - cout << endl << "}" << endl; - } -} - -/***** Struct Members *****/ - -Member* make_field(int lineno, string name, Expression* type) { - auto m = new Member(); - m->lineno = lineno; - m->tag = FieldMember; - m->u.field.name = new string(name); - m->u.field.type = type; - return m; -} - -/***** Declarations *****/ - -struct FunctionDefinition* -make_fun_def(int lineno, string name, Expression* ret_type, - Expression* param_pattern, Statement* body) { - struct FunctionDefinition* f = new struct FunctionDefinition(); - f->lineno = lineno; - f->name = name; - f->return_type = ret_type; - f->param_pattern = param_pattern; - f->body = body; - return f; -} - -Declaration* make_fun_decl(struct FunctionDefinition* f) { - Declaration* d = new Declaration(); - d->tag = FunctionDeclaration; - d->u.fun_def = f; - return d; -} - -Declaration* make_struct_decl(int lineno, string name, list* members) { - Declaration* d = new Declaration(); - d->tag = StructDeclaration; - d->u.struct_def = new struct StructDefinition(); - d->u.struct_def->lineno = lineno; - d->u.struct_def->name = new string(name); - d->u.struct_def->members = members; - return d; -} - -Declaration* make_choice_decl(int lineno, string name, - list >* alts) { - Declaration* d = new Declaration(); - d->tag = ChoiceDeclaration; - d->u.choice_def.lineno = lineno; - d->u.choice_def.name = new string(name); - d->u.choice_def.alternatives = alts; - return d; -} - -void print_params(VarTypes* ps) { - int i = 0; - for (auto iter = ps->begin(); iter != ps->end(); ++iter, ++i) { - if (i != 0) - cout << ", "; - print_exp(iter->second); - cout << ": "; - cout << iter->first; - } -} - -void print_var_decls(VarTypes* ps) { - int i = 0; - for (auto iter = ps->begin(); iter != ps->end(); ++iter, ++i) { - cout << "var "; - cout << iter->first; - cout << " : "; - print_exp(iter->second); - cout << "; "; - } -} - -void print_fun_def_depth(struct FunctionDefinition* f, int depth) { - cout << "fn " << f->name << " "; - print_exp(f->param_pattern); - cout << " -> "; - print_exp(f->return_type); - if (f->body) { - cout << " {" << endl; - print_stmt(f->body, depth); - cout << endl << "}" << endl; - } else { - cout << ";" << endl; - } -} - -void print_fun_def(struct FunctionDefinition* f) { - print_fun_def_depth(f, -1); -} - -void print_member(Member* m) { - switch (m->tag) { - case FieldMember: - cout << "var " << * m->u.field.name << " : "; - print_exp(m->u.field.type); - cout << ";" << endl; - break; - } -} - -void print_decl(Declaration* d) { - switch (d->tag) { - case FunctionDeclaration: - print_fun_def(d->u.fun_def); - break; - case StructDeclaration: - cout << "struct " << * d->u.struct_def->name << " {" << endl; - for (auto m = d->u.struct_def->members->begin(); - m != d->u.struct_def->members->end(); ++m) { - print_member(*m); - } - cout << "}" << endl; - break; - case ChoiceDeclaration: - cout << "choice " << * d->u.choice_def.name << " {" << endl; - for (auto a = d->u.choice_def.alternatives->begin(); - a != d->u.choice_def.alternatives->end(); ++a) { - cout << "alt " << a->first << " "; - print_exp(a->second); - cout << ";" << endl; - } - cout << "}" << endl; - break; - } -} - - -char *read_file(FILE* fp) -{ - char *fcontent = NULL; - int fsize = 0; - - if(fp) { - fseek(fp, 0, SEEK_END); - fsize = ftell(fp); - rewind(fp); - - fcontent = (char*) malloc(sizeof(char) * fsize); - fread(fcontent, 1, fsize, fp); - - fclose(fp); - } - return fcontent; -} diff --git a/executable-semantics/ast.h b/executable-semantics/ast.h deleted file mode 100644 index b54158e05f3b9..0000000000000 --- a/executable-semantics/ast.h +++ /dev/null @@ -1,238 +0,0 @@ -#ifndef AST_H -#define AST_H - -#include -#include -#include -#include -#include - -using std::string; -using std::list; -using std::vector; -using std::pair; - -/***** Utilities *****/ - -template -void print_list(list* ts, void(*printer)(T*), const char* sep) { - int i = 0; - for (auto iter = ts->begin(); iter != ts->end(); ++iter, ++i) { - if (i != 0) - printf("%s", sep); - printer(*iter); - } -} - -template -void print_vector(vector* ts, void(*printer)(T*), const char* sep) { - int i = 0; - for (auto iter = ts->begin(); iter != ts->end(); ++iter, ++i) { - if (i != 0) - printf("%s", sep); - printer(*iter); - } -} - -char *read_file(FILE* fp); - -extern char* input; - -/***** Forward Declarations *****/ - -struct LValue; -struct Expression; -struct Statement; -struct FunctionDefinition; - -typedef list< pair > VarTypes; - -/***** Expressions *****/ - -enum ExpressionKind { Variable, PatternVariable, Integer, Boolean, - PrimitiveOp, Call, Tuple, Index, GetField, - IntT, BoolT, TypeT, FunctionT, AutoT }; -enum Operator { Neg, Add, Sub, Not, And, Or, Eq }; - -struct Expression { - int lineno; - ExpressionKind tag; - union { - struct { string* name; } variable; - struct { Expression* aggregate; string* field; } get_field; - struct { Expression* aggregate; Expression* offset; } index; - struct { string* name; Expression* type; } pattern_variable; - int integer; - bool boolean; - struct { vector >* fields; } tuple; - struct { Operator operator_; vector* arguments; } primitive_op; - struct { Expression* function; Expression* argument; } call; - struct { Expression* parameter; Expression* return_type; } function_type; - } u; -}; - -Expression* make_var(int lineno, string var); -Expression* make_var_pat(int lineno, string var, Expression* type); -Expression* make_int(int lineno, int i); -Expression* make_bool(int lineno, bool b); -Expression* make_op(int lineno, Operator op, vector* args); -Expression* make_unop(int lineno, enum Operator op, Expression* arg); -Expression* make_binop(int lineno, enum Operator op, - Expression* arg1, Expression* arg2); -Expression* make_call(int lineno, Expression* fun, Expression* arg); -Expression* make_get_field(int lineno, Expression* exp, string field); -Expression* make_tuple(int lineno, vector >* args); -Expression* make_index(int lineno, Expression* exp, Expression* i); - -Expression* make_type_type(int lineno); -Expression* make_int_type(int lineno); -Expression* make_bool_type(int lineno); -Expression* make_fun_type(int lineno, Expression* param, Expression* ret); -Expression* make_auto_type(int lineno); - -void print_exp(Expression*); - -/***** Expression or Field List *****/ -/* - This is used in the parsing of tuples and parenthesized expressions. - */ - -enum ExpOrFieldListKind { Exp, FieldList }; - -struct ExpOrFieldList { - ExpOrFieldListKind tag; - union { - Expression* exp; - list >* fields; - } u; -}; - -ExpOrFieldList* make_exp(Expression* exp); -ExpOrFieldList* make_field_list(list >* fields); -ExpOrFieldList* cons_field(ExpOrFieldList* e1, ExpOrFieldList* e2); - -/***** Statements *****/ - -enum StatementKind { ExpressionStatement, Assign, VariableDefinition, - If, Return, Sequence, Block, While, Break, Continue, - Match }; - -struct Statement { - int lineno; - StatementKind tag; - union { - Expression* exp; - struct { Expression* lhs; Expression* rhs; } assign; - struct { Expression* pat; Expression* init; } variable_definition; - struct { Expression* cond; Statement* then_; Statement* else_; } if_stmt; - Expression* return_stmt; - struct { Statement* stmt; Statement* next; } sequence; - struct { Statement* stmt; } block; - struct { Expression* cond; Statement* body; } while_stmt; - struct { - Expression* exp; - list< pair >* clauses; - } match_stmt; - } u; -}; - -Statement* make_exp_stmt(int lineno, Expression* exp); -Statement* make_assign(int lineno, Expression* lhs, Expression* rhs); -Statement* make_var_def(int lineno, Expression* pat, Expression* init); -Statement* make_if(int lineno, Expression* cond, Statement* then_, - Statement* else_); -Statement* make_return(int lineno, Expression* e); -Statement* make_seq(int lineno, Statement* s1, Statement* s2); -Statement* make_block(int lineno, Statement* s); -Statement* make_while(int lineno, Expression* cond, Statement* body); -Statement* make_break(int lineno); -Statement* make_continue(int lineno); -Statement* make_match(int lineno, Expression* exp, - list< pair >* clauses); - -void print_stmt(Statement*, int); - -/***** Function Definitions *****/ - -struct FunctionDefinition { - int lineno; - string name; - Expression* param_pattern; - Expression* return_type; - Statement* body; -}; - -/***** Struct Members *****/ - -enum MemberKind { FieldMember }; - -struct Member { - int lineno; - MemberKind tag; - union { - struct { string* name; Expression* type; } field; - } u; -}; - -Member* make_field(int lineno, string name, Expression* type); - -/***** Declarations *****/ - -struct StructDefinition { - int lineno; - string* name; - list* members; -}; - -enum DeclarationKind { FunctionDeclaration, StructDeclaration, - ChoiceDeclaration }; - -struct Declaration { - DeclarationKind tag; - union { - struct FunctionDefinition* fun_def; - struct StructDefinition* struct_def; - struct { - int lineno; - string* name; - list >* alternatives; - } choice_def; - } u; -}; - - -struct FunctionDefinition* -make_fun_def(int lineno, string name, Expression* ret_type, - Expression* param, Statement* body); -void print_fun_def(struct FunctionDefinition*); -void print_fun_def_depth(struct FunctionDefinition*, int); - -Declaration* make_fun_decl(struct FunctionDefinition* f); -Declaration* make_struct_decl(int lineno, string name, list* members); -Declaration* make_choice_decl(int lineno, string name, - list >* alts); - -void print_decl(Declaration*); - -void print_string(string* s); - -template -T find_field(string field, vector >* inits) { - for (auto i = inits->begin(); i != inits->end(); ++i) { - if (i->first == field) - return i->second; - } - throw std::domain_error(field); -} - -template -T find_alist(string field, list >* inits) { - for (auto i = inits->begin(); i != inits->end(); ++i) { - if (i->first == field) - return i->second; - } - throw std::domain_error(field); -} - - -#endif diff --git a/executable-semantics/cons_list.h b/executable-semantics/cons_list.h deleted file mode 100644 index 75ab84813de39..0000000000000 --- a/executable-semantics/cons_list.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CONS_LIST_H -#define CONS_LIST_H - -template -struct Cons { - T curr; - Cons* next; - Cons(T e, Cons* n) : curr(e), next(n) { } -}; - -template -Cons* cons(const T& x, Cons* ls) { - return new Cons(x, ls); -} - -template -unsigned int length(Cons* ls) { - if (ls) { - return 1 + length(ls->next); - } else { - return 0; - } -} - - -#endif diff --git a/executable-semantics/examples/block1.6c b/executable-semantics/examples/block1.6c deleted file mode 100644 index 1ef3155006898..0000000000000 --- a/executable-semantics/examples/block1.6c +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Int { - var Int: x = 0; - { - var Int: x = 1; - } - return x; -} diff --git a/executable-semantics/examples/break1.6c b/executable-semantics/examples/break1.6c deleted file mode 100644 index 2323045c185a4..0000000000000 --- a/executable-semantics/examples/break1.6c +++ /dev/null @@ -1,10 +0,0 @@ -fn main() -> Int { - var Int: x = 2; - while (true) { - if (x == 0) - break; - else - x = x - 1; - } - return x; -} \ No newline at end of file diff --git a/executable-semantics/examples/choice1.6c b/executable-semantics/examples/choice1.6c deleted file mode 100644 index ef10b189b1b0d..0000000000000 --- a/executable-semantics/examples/choice1.6c +++ /dev/null @@ -1,28 +0,0 @@ -choice Ints { - None(); - One(Int); - Two(Int,Int); -} - -fn main() -> Int { - var auto: x = Ints.None(); - var auto: y = Ints.One(42); - var auto: n = 0; - match (y) { - case Ints.None() => - n = n + 2; - case Ints.One(auto: x) => - n = x + 1 - 42; - case Ints.Two(auto: a, auto: b) => - n = 2; - } - match (x) { - case Ints.One(auto: x) => - n = x + 2; - case Ints.None() => - n = n - 1; - case Ints.Two(auto: x, auto: y) => - n = 5; - } - return n; -} \ No newline at end of file diff --git a/executable-semantics/examples/continue1.6c b/executable-semantics/examples/continue1.6c deleted file mode 100644 index 2d454e099c6a0..0000000000000 --- a/executable-semantics/examples/continue1.6c +++ /dev/null @@ -1,9 +0,0 @@ -fn main() -> Int { - var auto: x = 2; - while (not (x == 0)) { - x = x - 1; - continue; - x = x + 1; - } - return x; -} \ No newline at end of file diff --git a/executable-semantics/examples/fun-recur.6c b/executable-semantics/examples/fun-recur.6c deleted file mode 100644 index d4da3022557e5..0000000000000 --- a/executable-semantics/examples/fun-recur.6c +++ /dev/null @@ -1,10 +0,0 @@ -fn f(Int: x) -> Int { - if (x == 0) - return x; - else - return f(x - 1); -} - -fn main() -> Int { - return f(2); -} diff --git a/executable-semantics/examples/fun1.6c b/executable-semantics/examples/fun1.6c deleted file mode 100644 index ee7ec04901305..0000000000000 --- a/executable-semantics/examples/fun1.6c +++ /dev/null @@ -1,7 +0,0 @@ -fn f(Int: x) -> Int { - return x - 1; -} - -fn main() -> Int { - return f(1); -} \ No newline at end of file diff --git a/executable-semantics/examples/fun2.6c b/executable-semantics/examples/fun2.6c deleted file mode 100644 index 94e5d5f5f1ed3..0000000000000 --- a/executable-semantics/examples/fun2.6c +++ /dev/null @@ -1,14 +0,0 @@ -// This tests the call-by-value aspect of parameter passing. -// This makes sure that when the value in `x` dies, -// it does not cause the value in `a` to also die. - -fn f(Int: x) -> Int { - return 0; -} - -fn main() -> Int { - var Int: a = 0; var Int: b = 1; - f(a); - b = a; - return b; -} \ No newline at end of file diff --git a/executable-semantics/examples/fun3.6c b/executable-semantics/examples/fun3.6c deleted file mode 100644 index b977e2ef2a8ee..0000000000000 --- a/executable-semantics/examples/fun3.6c +++ /dev/null @@ -1,8 +0,0 @@ -// Test multiple arguments -fn f(Int: x, Int: y) -> Int { - return x + y; -} - -fn main() -> Int { - return f(2,3) - 5; -} \ No newline at end of file diff --git a/executable-semantics/examples/fun4.6c b/executable-semantics/examples/fun4.6c deleted file mode 100644 index dadf3b92a7838..0000000000000 --- a/executable-semantics/examples/fun4.6c +++ /dev/null @@ -1,7 +0,0 @@ -// Test empty parameters and return type -fn f() { } - -fn main() -> Int { - f(); - return 0; -} \ No newline at end of file diff --git a/executable-semantics/examples/fun5.6c b/executable-semantics/examples/fun5.6c deleted file mode 100644 index 189d36f898578..0000000000000 --- a/executable-semantics/examples/fun5.6c +++ /dev/null @@ -1,5 +0,0 @@ -fn add(Int: x, Int: y) => x + y; - -fn main() -> Int { - return add(1, 2) - 3; -} \ No newline at end of file diff --git a/executable-semantics/examples/fun6-fail-type.6c b/executable-semantics/examples/fun6-fail-type.6c deleted file mode 100644 index 000af41653cd5..0000000000000 --- a/executable-semantics/examples/fun6-fail-type.6c +++ /dev/null @@ -1,7 +0,0 @@ -fn f(Int: x, Int: y) -> Int { return x + y; } - -fn main() -> Int { - var (Int, Int): xy = (1, 2); - // should fail to type-check - return f(xy); -} \ No newline at end of file diff --git a/executable-semantics/examples/funptr1.6c b/executable-semantics/examples/funptr1.6c deleted file mode 100644 index 3e02d6462150a..0000000000000 --- a/executable-semantics/examples/funptr1.6c +++ /dev/null @@ -1,8 +0,0 @@ -fn add1(Int: x) -> Int { - return x + 1; -} - -fn main() -> Int { - var fnty(Int)->Int: f = add1; - return f(-1); -} diff --git a/executable-semantics/examples/match-int-default.6c b/executable-semantics/examples/match-int-default.6c deleted file mode 100644 index cd5e36f3f6809..0000000000000 --- a/executable-semantics/examples/match-int-default.6c +++ /dev/null @@ -1,11 +0,0 @@ -fn main() -> Int { - var auto: t = 5; - match (t) { - case 3 => - return -1; - case 4 => - return -1; - default => - return 0; - } -} \ No newline at end of file diff --git a/executable-semantics/examples/match-int.6c b/executable-semantics/examples/match-int.6c deleted file mode 100644 index 9dd5d57b2f46d..0000000000000 --- a/executable-semantics/examples/match-int.6c +++ /dev/null @@ -1,9 +0,0 @@ -fn main() -> Int { - var auto: t = 5; - match (t) { - case 5 => - return 0; - default => - return 1; - } -} \ No newline at end of file diff --git a/executable-semantics/examples/match-type.6c b/executable-semantics/examples/match-type.6c deleted file mode 100644 index 4ed15d7786d7e..0000000000000 --- a/executable-semantics/examples/match-type.6c +++ /dev/null @@ -1,17 +0,0 @@ -fn main() -> Int { - var auto: t = fnty (Int,Int); - var Int: x = 0; -// match (t) { -// case fnty(Int: x, Int: y): z => -// x = x - 1; -// } - match (t) { - case fnty(Int,Int): z => - x = x + 1; - } - match (t) { - case fnty(Type: a,Type: b) => - x = x - 1; - } - return x; -} \ No newline at end of file diff --git a/executable-semantics/examples/next.6c b/executable-semantics/examples/next.6c deleted file mode 100644 index 796f696289c1b..0000000000000 --- a/executable-semantics/examples/next.6c +++ /dev/null @@ -1,5 +0,0 @@ -fn main () -> Int -{ - var Int: x = 0; - return x; -} diff --git a/executable-semantics/examples/pattern-init.6c b/executable-semantics/examples/pattern-init.6c deleted file mode 100644 index d5e6f8d96f392..0000000000000 --- a/executable-semantics/examples/pattern-init.6c +++ /dev/null @@ -1,4 +0,0 @@ -fn main() -> Int { - var (auto: x, auto: y) = (2, 3); - return y - x - 1; -} \ No newline at end of file diff --git a/executable-semantics/examples/record1.6c b/executable-semantics/examples/record1.6c deleted file mode 100644 index e57abe5f31574..0000000000000 --- a/executable-semantics/examples/record1.6c +++ /dev/null @@ -1,5 +0,0 @@ -fn main() -> Int { - var (.x = Int, .y = Int): t2 = (.y = 5, .x = 2); - t2.y = 3; - return t2.y - t2.x - 1; // 3 - 2 - 1 -} \ No newline at end of file diff --git a/executable-semantics/examples/struct1.6c b/executable-semantics/examples/struct1.6c deleted file mode 100644 index 668cf951a2aff..0000000000000 --- a/executable-semantics/examples/struct1.6c +++ /dev/null @@ -1,9 +0,0 @@ -struct Point { - var Int: x; - var Int: y; -} - -fn main() -> Int { - var auto: p = Point(.x = 1, .y = 2); - return p.y - p.x - 1; -} \ No newline at end of file diff --git a/executable-semantics/examples/struct2.6c b/executable-semantics/examples/struct2.6c deleted file mode 100644 index 0f284df2d0178..0000000000000 --- a/executable-semantics/examples/struct2.6c +++ /dev/null @@ -1,11 +0,0 @@ -struct Point { - var Int: x; - var Int: y; -} - -fn main() -> Int { - var auto: p1 = Point(.x = 1, .y = 2); - var auto: p2 = p1; - p2.x = 3; - return p1.x - 1; -} \ No newline at end of file diff --git a/executable-semantics/examples/struct3.6c b/executable-semantics/examples/struct3.6c deleted file mode 100644 index c73c93ce207c8..0000000000000 --- a/executable-semantics/examples/struct3.6c +++ /dev/null @@ -1,8 +0,0 @@ -struct Point { - var Int: x; - var Int: y; -} - -fn main() -> Int { - return Point(.x = 1, .y = 2).x - 1; -} \ No newline at end of file diff --git a/executable-semantics/examples/tuple-assign.6c b/executable-semantics/examples/tuple-assign.6c deleted file mode 100644 index 483f7c935d08c..0000000000000 --- a/executable-semantics/examples/tuple-assign.6c +++ /dev/null @@ -1,6 +0,0 @@ -fn main() -> Int { - var auto: x = 0; - var auto: y = 1; - (x, y) = (5, -5); - return x + y; -} \ No newline at end of file diff --git a/executable-semantics/examples/tuple-match.6c b/executable-semantics/examples/tuple-match.6c deleted file mode 100644 index 2c417ab3682e2..0000000000000 --- a/executable-semantics/examples/tuple-match.6c +++ /dev/null @@ -1,7 +0,0 @@ -fn main() -> Int { - var auto: t = (5, 2); - match (t) { - case (auto: a, auto: b) => - return a + b - 7; - } -} \ No newline at end of file diff --git a/executable-semantics/examples/tuple1.6c b/executable-semantics/examples/tuple1.6c deleted file mode 100644 index bf97070d4aa9b..0000000000000 --- a/executable-semantics/examples/tuple1.6c +++ /dev/null @@ -1,6 +0,0 @@ -fn main() -> Int { - var Int: x = (1); - var (Int,Int): t2 = (5, 2); - t2[0] = 3; - return t2[0] - t2[1] - x; -} \ No newline at end of file diff --git a/executable-semantics/examples/tuple2.6c b/executable-semantics/examples/tuple2.6c deleted file mode 100644 index 326c4f8f1632b..0000000000000 --- a/executable-semantics/examples/tuple2.6c +++ /dev/null @@ -1,4 +0,0 @@ -fn main() -> Int { - var (Int,): t = (5,); - return t[0] - 5; -} \ No newline at end of file diff --git a/executable-semantics/examples/undef1.6c b/executable-semantics/examples/undef1.6c deleted file mode 100644 index dde42e3451ec6..0000000000000 --- a/executable-semantics/examples/undef1.6c +++ /dev/null @@ -1,14 +0,0 @@ -// The behavior of this program is undefined because it tries to -// access a local variable after its lifetime is over. - -fn f() -> Int* { - var int: x; - x = 0; - return &x; -} - -fn main() -> int { - var Ptr(int): p; - p = f(); - return *p; -} diff --git a/executable-semantics/examples/undef2.6c b/executable-semantics/examples/undef2.6c deleted file mode 100644 index 567d8bb048aec..0000000000000 --- a/executable-semantics/examples/undef2.6c +++ /dev/null @@ -1,9 +0,0 @@ -// Use-after-free. - -fun main() -> Int { - var Ptr(int): p; - p = malloc(Int); - *p = 0; - free(p); - return *p; -} diff --git a/executable-semantics/examples/while1.6c b/executable-semantics/examples/while1.6c deleted file mode 100644 index a7f526a0c9103..0000000000000 --- a/executable-semantics/examples/while1.6c +++ /dev/null @@ -1,6 +0,0 @@ -fn main() -> Int { - var auto: x = 2; - while (not (x == 0)) - x = x - 1; - return x; -} diff --git a/executable-semantics/examples/zero.6c b/executable-semantics/examples/zero.6c deleted file mode 100644 index 2c5c5f1891403..0000000000000 --- a/executable-semantics/examples/zero.6c +++ /dev/null @@ -1,3 +0,0 @@ -fn main() -> Int { - return 0; -} \ No newline at end of file diff --git a/executable-semantics/interp.cc b/executable-semantics/interp.cc deleted file mode 100644 index 1872996cfb3e1..0000000000000 --- a/executable-semantics/interp.cc +++ /dev/null @@ -1,1704 +0,0 @@ -#include -#include -#include -#include -#include "interp.h" -#include "typecheck.h" - -using std::vector; -using std::map; -using std::cout; -using std::cerr; -using std::endl; - -State* state; - -Env* pattern_match(Value* pat, Value* val, Env*, list&, int); -void handle_value(); - -/***** Value Operations *****/ - -int to_integer(Value* v) { - switch (v->tag) { - case IntV: - return v->u.integer; - default: - cerr << "expected an integer, not "; - print_value(v, cerr); - exit(-1); - } -} - -void check_alive(Value* v, int lineno) { - if (! v->alive) { - cerr << lineno << ": undefined behavior: access to dead value "; - print_value(v, cerr); - cerr << endl; - exit(-1); - } -} - -Value* make_int_val(int i) { - Value* v = new Value(); - v->alive = true; - v->tag = IntV; - v->u.integer = i; - return v; -} - -Value* make_bool_val(bool b) { - Value* v = new Value(); - v->alive = true; - v->tag = BoolV; - v->u.boolean = b; - return v; -} - -Value* make_fun_val(string name, Value* param, Statement* body) { - Value* v = new Value(); - v->alive = true; - v->tag = FunV; - v->u.fun.name = new string(name); - v->u.fun.param = param; - v->u.fun.body = body; - return v; -} - -Value* make_ptr_val(address addr) { - Value* v = new Value(); - v->alive = true; - v->tag = PtrV; - v->u.ptr = addr; - return v; -} - -Value* make_struct_val(Value* type, Value* inits) { - Value* v = new Value(); - v->alive = true; - v->tag = StructV; - v->u.struct_val.type = type; - v->u.struct_val.inits = inits; - return v; -} - -Value* make_tuple_val(vector >* elts) { - Value* v = new Value(); - v->alive = true; - v->tag = TupleV; - v->u.tuple.elts = elts; - return v; -} - -Value* make_alt_val(string alt_name, string choice_name, Value* arg) { - Value* v = new Value(); - v->alive = true; - v->tag = AltV; - v->u.alt.alt_name = new string(alt_name); - v->u.alt.choice_name = new string(choice_name); - v->u.alt.arg = arg; - return v; -} - -Value* make_alt_cons(string alt_name, string choice_name) { - Value* v = new Value(); - v->alive = true; - v->tag = AltConsV; - v->u.alt.alt_name = new string(alt_name); - v->u.alt.choice_name = new string(choice_name); - return v; -} - -Value* make_var_pat_val(string name, Value* type) { - Value* v = new Value(); - v->alive = true; - v->tag = VarPatV; - v->u.var_pat.name = new string(name); - v->u.var_pat.type = type; - return v; -} - -Value* make_var_type_val(string name) { - Value* v = new Value(); - v->alive = true; - v->tag = VarTV; - v->u.var_type = new string(name); - return v; -} - -Value* make_int_type_val() { - Value* v = new Value(); - v->alive = true; - v->tag = IntTV; - return v; -} - -Value* make_bool_type_val() { - Value* v = new Value(); - v->alive = true; - v->tag = BoolTV; - return v; -} - -Value* make_type_type_val() { - Value* v = new Value(); - v->alive = true; - v->tag = TypeTV; - return v; -} - -Value* make_auto_type_val() { - Value* v = new Value(); - v->alive = true; - v->tag = AutoTV; - return v; -} - -Value* make_fun_type_val(Value* param, Value* ret) { - Value* v = new Value(); - v->alive = true; - v->tag = FunctionTV; - v->u.fun_type.param = param; - v->u.fun_type.ret = ret; - return v; -} - -Value* make_ptr_type_val(Value* type) { - Value* v = new Value(); - v->alive = true; - v->tag = PointerTV; - v->u.ptr_type.type = type; - return v; -} - -Value* make_struct_type_val(string name, VarValues* fields, VarValues* methods){ - Value* v = new Value(); - v->alive = true; - v->tag = StructTV; - v->u.struct_type.name = new string(name); - v->u.struct_type.fields = fields; - v->u.struct_type.methods = methods; - return v; -} - -Value* make_tuple_type_val(VarValues* fields) { - Value* v = new Value(); - v->alive = true; - v->tag = TupleTV; - v->u.tuple_type.fields = fields; - return v; -} - -Value* make_void_type_val() { - Value* v = new Value(); - v->alive = true; - v->tag = TupleTV; - v->u.tuple_type.fields = new VarValues(); - return v; -} - - -Value* make_choice_type_val(string* name, list >* alts){ - Value* v = new Value(); - v->alive = true; - v->tag = ChoiceTV; - v->u.choice_type.name = name; - v->u.choice_type.alternatives = alts; - return v; -} - -/**** Auxiliary Functions ****/ - - -address allocate_value(Value* v) { - // Putting the following two side effects together in this function - // ensures that we don't do anything else in between, which is really bad! - // Consider whether to include a copy of the input v in this function - // or to leave it up to the caller. - address a = state->heap.size(); - state->heap.push_back(v); - return a; -} - -Value* copy_val(Value* val, int lineno) { - check_alive(val, lineno); - switch (val->tag) { - case TupleV: { - auto elts = new vector >(); - for (auto i = val->u.tuple.elts->begin(); - i != val->u.tuple.elts->end(); ++i) { - Value* elt = copy_val(state->heap[i->second], lineno); - elts->push_back(make_pair(i->first, allocate_value(elt))); - } - return make_tuple_val(elts); - } - case AltV: { - Value* arg = copy_val(val->u.alt.arg, lineno); - return make_alt_val(*val->u.alt.alt_name, *val->u.alt.choice_name, arg); - } - case StructV: { - Value* inits = copy_val(val->u.struct_val.inits, lineno); - return make_struct_val(val->u.struct_val.type, inits); - } - case IntV: - return make_int_val(val->u.integer); - case BoolV: - return make_bool_val(val->u.boolean); - case FunV: - return make_fun_val(*val->u.fun.name, val->u.fun.param, val->u.fun.body); - case PtrV: - return make_ptr_val(val->u.ptr); - case FunctionTV: - return make_fun_type_val(copy_val(val->u.fun_type.param, lineno), - copy_val(val->u.fun_type.ret, lineno)); - - case PointerTV: - return make_ptr_type_val(copy_val(val->u.ptr_type.type, lineno)); - case IntTV: - return make_int_type_val(); - case BoolTV: - return make_bool_type_val(); - case TypeTV: - return make_type_type_val(); - case VarTV: - return make_var_type_val(* val->u.var_type); - case AutoTV: - return make_auto_type_val(); - case TupleTV: { - auto new_fields = new VarValues(); - for (auto i = val->u.tuple_type.fields->begin(); - i != val->u.tuple_type.fields->end(); ++i) { - auto v = copy_val(i->second, lineno); - new_fields->push_back(make_pair(i->first, v)); - } - return make_tuple_type_val(new_fields); - } - case StructTV: case ChoiceTV: - case VarPatV: case AltConsV: - return val; // no need to copy these because they are immutable? - // No, they need to be copied so they don't get killed. -Jeremy - } -} - -void kill_value(Value* val) { - val->alive = false; - switch (val->tag) { - case AltV: - kill_value(val->u.alt.arg); - break; - case StructV: - kill_value(val->u.struct_val.inits); - break; - case TupleV: - for (auto i = val->u.tuple.elts->begin(); - i != val->u.tuple.elts->end(); ++i) { - if (state->heap[i->second]->alive) - kill_value(state->heap[i->second]); - else { - cerr << "runtime error, killing an already dead value" << endl; - exit(-1); - } - } - break; - default: - break; - } -} - -void print_env(Env* env, std::ostream& out) { - if (env) { - cout << env->key << ": "; - print_value(state->heap[env->value], out); - cout << ", "; - print_env(env->next, out); - } -} - -void print_value(Value* val, std::ostream& out) { - if (! val->alive) { - out << "!!"; - } - switch (val->tag) { - case AltConsV: { - out << * val->u.alt_cons.choice_name << "." << * val->u.alt_cons.alt_name; - break; - } - case VarPatV: { - print_value(val->u.var_pat.type, out); - out << ": " << *val->u.var_pat.name; - break; - } - case AltV: { - out << "alt " - << * val->u.alt.choice_name - << "." - << * val->u.alt.alt_name - << " "; - print_value(val->u.alt.arg, out); - break; - } - case StructV: { - out << * val->u.struct_val.type->u.struct_type.name; - print_value(val->u.struct_val.inits, out); - break; - } - case TupleV: { - out << "("; - int i = 0; - for (auto elt = val->u.tuple.elts->begin(); - elt != val->u.tuple.elts->end(); ++elt, ++i) { - if (i != 0) - out << ", "; - out << elt->first << " = "; - print_value(state->heap[elt->second], out); - out << "@" << elt->second; - } - out << ")"; - break; - } - case IntV: - out << val->u.integer; - break; - case BoolV: - out << std::boolalpha; - out << val->u.boolean; - break; - case FunV: - out << "fun<" << * val->u.fun.name << ">"; - break; - case PtrV: - out << "ptr<" << val->u.ptr << ">"; - break; - case BoolTV: - out << "Bool"; - break; - case IntTV: - out << "Int"; - break; - case TypeTV: - out << "Type"; - break; - case AutoTV: - out << "auto"; - break; - case PointerTV: - out << "Ptr("; - print_value(val->u.ptr_type.type, out); - out << ")"; - break; - case FunctionTV: - out << "fn "; - print_value(val->u.fun_type.param, out); - out << " -> "; - print_value(val->u.fun_type.ret, out); - break; - case VarTV: - out << * val->u.var_type; - break; - case TupleTV: { - out << "Tuple("; - int i = 0; - for (auto elt = val->u.tuple_type.fields->begin(); - elt != val->u.tuple_type.fields->end(); ++elt, ++i) { - if (i != 0) - out << ", "; - out << elt->first << " = "; - print_value(elt->second, out); - } - out << ")"; - break; - } - case StructTV: - out << "struct " << * val->u.struct_type.name; - break; - case ChoiceTV: - out << "choice " << * val->u.choice_type.name; - break; - } -} - -/***** Action Operations *****/ - -void print_act(Action* act, std::ostream& out) { - switch (act->tag) { - case DeleteTmpAction: - cout << "delete_tmp(" << act->u.delete_ << ")"; - break; - case ExpToLValAction: - out << "exp=>lval"; - break; - case LValAction: - case ExpressionAction: - print_exp(act->u.exp); - break; - case StatementAction: - print_stmt(act->u.stmt, 1); - break; - case ValAction: - print_value(act->u.val, out); - break; - } - out << "<" << act->pos << ">"; - if (act->results.size() > 0) { - out << "("; - for (auto iter = act->results.begin(); iter != act->results.end(); ++iter) { - if (*iter) - print_value(*iter, out); - out << ","; - } - out << ")"; - } -} - -void print_act_list(Cons* ls, std::ostream& out) { - if (ls) { - print_act(ls->curr, out); - if (ls->next) { - out << " :: "; - print_act_list(ls->next, out); - } - } -} - -Action* make_exp_act(Expression* e) { - Action* act = new Action(); - act->tag = ExpressionAction; - act->u.exp = e; - act->pos = -1; - return act; -} - -Action* make_lval_act(Expression* e) { - Action* act = new Action(); - act->tag = LValAction; - act->u.exp = e; - act->pos = -1; - return act; -} - -Action* make_stmt_act(Statement* s) { - Action* act = new Action(); - act->tag = StatementAction; - act->u.stmt = s; - act->pos = -1; - return act; -} - -Action* make_val_act(Value* v) { - Action* act = new Action(); - act->tag = ValAction; - act->u.val = v; - act->pos = -1; - return act; -} - -Action* make_exp_to_lval_act() { - Action* act = new Action(); - act->tag = ExpToLValAction; - act->pos = -1; - return act; -} - -Action* make_delete_act(address a) { - Action* act = new Action(); - act->tag = DeleteTmpAction; - act->pos = -1; - act->u.delete_ = a; - return act; -} - -/***** Frame and State Operations *****/ - -void print_frame(Frame* frame, std::ostream& out) { - out << frame->name; - out << "{"; - print_act_list(frame->todo, out); - out << "}"; -} - -void print_stack(Cons* ls, std::ostream& out) { - if (ls) { - print_frame(ls->curr, out); - if (ls->next) { - out << " :: "; - print_stack(ls->next, out); - } - } -} - -void print_heap(vector& heap, std::ostream& out) { - for (auto iter = heap.begin(); iter != heap.end(); ++iter) { - if (*iter) { - print_value(*iter, out); - } else { - out << "_"; - } - out << ", "; - } -} - -Env* current_env(State* state) { - Frame* frame = state->stack->curr; - return frame->scopes->curr->env; -} - -void print_state(std::ostream& out) { - out << "{" << endl; - out << "stack: "; - print_stack(state->stack, out); - out << endl << "heap: "; - print_heap(state->heap, out); - out << endl << "env: "; - print_env(current_env(state), out); - out << endl << "}" << endl; -} - -/***** Auxilliary Functions *****/ - - -int val_to_int(Value* v, int lineno) { - check_alive(v, lineno); - switch (v->tag) { - case IntV: - return v->u.integer; - default: - cerr << lineno << ": runtime error: expected an integer" << endl; - exit(-1); - } -} - -int val_to_bool(Value* v, int lineno) { - check_alive(v, lineno); - switch (v->tag) { - case BoolV: - return v->u.boolean; - default: - cerr << "runtime type error: expected a Boolean" << endl; - exit(-1); - } -} - -address val_to_ptr(Value* v, int lineno) { - check_alive(v, lineno); - switch (v->tag) { - case PtrV: - return v->u.ptr; - default: - cerr << "runtime type error: expected a pointer, not "; - print_value(v, cerr); - cerr << endl; - exit(-1); - } -} - -bool fields_value_equal(VarValues* ts1, VarValues* ts2, int lineno) { - if (ts1->size() == ts2->size()) { - for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1) { - try { - auto t2 = find_alist(iter1->first, ts2); - if (! value_equal(iter1->second, t2, lineno)) - return false; - } catch (std::domain_error de) { - return false; - } - } - return true; - } else { - return false; - } -} - -bool value_equal(Value* v1, Value* v2, int lineno) { - check_alive(v1, lineno); - check_alive(v2, lineno); - return (v1->tag == IntV && v2->tag == IntV && v1->u.integer == v2->u.integer) - || (v1->tag == BoolV && v2->tag == BoolV && v1->u.boolean == v2->u.boolean) - || (v1->tag == PtrV && v2->tag == PtrV && v1->u.ptr == v2->u.ptr) - || (v1->tag == FunV && v2->tag == FunV && v1->u.fun.body == v2->u.fun.body) - || (v1->tag == TupleV && v2->tag == TupleV - && fields_value_equal(v1->u.tuple_type.fields, - v2->u.tuple_type.fields, lineno)) - // TODO: struct and alternative values - || type_equal(v1, v2); -} - -Value* eval_prim(Operator op, const vector& args, int lineno) { - switch (op) { - case Neg: - return make_int_val(- val_to_int(args[0], lineno)); - case Add: - return make_int_val(val_to_int(args[0], lineno) - + val_to_int(args[1], lineno)); - case Sub: - return make_int_val(val_to_int(args[0], lineno) - - val_to_int(args[1], lineno)); - case Not: - return make_bool_val(! val_to_bool(args[0], lineno)); - case And: - return make_bool_val(val_to_bool(args[0], lineno) - && val_to_bool(args[1], lineno)); - case Or: - return make_bool_val(val_to_bool(args[0], lineno) - || val_to_bool(args[1], lineno)); - case Eq: - return make_bool_val(value_equal(args[0], args[1], lineno)); - } -} - -Env* globals; - -void init_globals(list* fs) { - globals = 0; - for (auto iter = fs->begin(); iter != fs->end(); ++iter) { - switch ((*iter)->tag) { - case ChoiceDeclaration: { - auto d = *iter; - auto alts = new VarValues(); - for (auto i = d->u.choice_def.alternatives->begin(); - i != d->u.choice_def.alternatives->end(); ++i) { - auto t = to_type(d->u.choice_def.lineno, interp_exp(0, i->second)); - alts->push_back(make_pair(i->first, t)); - } - auto ct = make_choice_type_val(d->u.choice_def.name, alts); - auto a = allocate_value(ct); - globals = new Env(* d->u.choice_def.name, a, globals); - break; - } - case StructDeclaration: { - auto d = *iter; - auto fields = new VarValues(); - auto methods = new VarValues(); - for (auto i = d->u.struct_def->members->begin(); - i != d->u.struct_def->members->end(); ++i) { - switch ((*i)->tag) { - case FieldMember: { - auto t = to_type(d->u.struct_def->lineno, - interp_exp(0, (*i)->u.field.type)); - fields->push_back(make_pair(* (*i)->u.field.name, t)); - break; - } - } - } - auto st = make_struct_type_val(*d->u.struct_def->name, fields, methods); - auto a = allocate_value(st); - globals = new Env(* d->u.struct_def->name, a, globals); - break; - } - case FunctionDeclaration: { - struct FunctionDefinition* fun = (*iter)->u.fun_def; - Env* env = 0; - VarValues* implicit_params = 0; - auto pt = interp_exp(env, fun->param_pattern); - auto f = make_fun_val(fun->name, pt, fun->body); - address a = allocate_value(f); - globals = new Env(fun->name, a, globals); - break; - } - } - } -} - -// { S, H} -> { { C, E, F} :: S, H} -// where C is the body of the function, -// E is the environment (functions + parameters + locals) -// F is the function -void call_function(int lineno, vector operas, State* state) { - check_alive(operas[0], lineno); - switch (operas[0]->tag) { - case FunV: { - Env* env = globals; - // Bind arguments to parameters - list params; - env = pattern_match(operas[0]->u.fun.param, operas[1], env, params, lineno); - if (!env) { - cerr << "internal error in call_function, pattern match failed" << endl; - exit(-1); - } - // Create the new frame and push it on the stack - Scope* scope = new Scope(env, params); - Frame* frame = new Frame(* operas[0]->u.fun.name, - cons(scope, (Cons*)0), - cons(make_stmt_act(operas[0]->u.fun.body), - (Cons*)0)); - state->stack = cons(frame, state->stack); - break; - } - case StructTV: { - Value* arg = copy_val(operas[1], lineno); - Value* sv = make_struct_val(operas[0], arg); - Frame* frame = state->stack->curr; - frame->todo = cons(make_val_act(sv), frame->todo); - break; - } - case AltConsV: { - Value* arg = copy_val(operas[1], lineno); - Value* av = make_alt_val(* operas[0]->u.alt_cons.alt_name, - * operas[0]->u.alt_cons.choice_name, - arg); - Frame* frame = state->stack->curr; - frame->todo = cons(make_val_act(av), frame->todo); - break; - } - default: - cerr << lineno << ": in call, expected a function, not "; - print_value(operas[0], cerr); - cerr << endl; - exit(-1); - } -} - -void kill_scope(int lineno, Scope* scope) { - for (auto l = scope->locals.begin(); l != scope->locals.end(); ++l) { - address a = lookup(lineno, scope->env, *l, print_error_string); - kill_value(state->heap[a]); - } -} - -void kill_locals(int lineno, Frame* frame) { - Cons* scopes = frame->scopes; - for (Scope* scope = scopes->curr; scopes; scopes = scopes->next) { - kill_scope(lineno, scope); - } -} - -void create_tuple(Frame* frame, Action* act, Expression* exp) { - // { { (v1,...,vn) :: C, E, F} :: S, H} - // -> { { `(v1,...,vn) :: C, E, F} :: S, H} - auto elts = new vector>(); - auto f = act->u.exp->u.tuple.fields->begin(); - for (auto i = act->results.begin(); i != act->results.end(); ++i, ++f) { - address a = allocate_value(*i); // copy? - elts->push_back(make_pair(f->first, a)); - } - Value* tv = make_tuple_val(elts); - frame->todo = cons(make_val_act(tv), frame->todo->next); -} - -Value* to_value(Expression* value) { - switch (value->tag) { - case Integer: - return make_int_val(value->u.integer); - case Boolean: - return make_bool_val(value->u.boolean); - case IntT: - return make_int_type_val(); - case BoolT: - return make_bool_type_val(); - case TypeT: - return make_type_type_val(); - case FunctionT: - // instead add to patterns? - default: - cerr << "internal error in to_value, didn't expect "; - print_exp(value); - cerr << endl; - exit(-1); - } -} - - -// -// Returns 0 if the value doesn't match the pattern -// -Env* pattern_match(Value* p, Value* v, Env* env, - list& vars, int lineno) { - cout << "pattern_match("; - print_value(p, cout); - cout << ", "; - print_value(v, cout); - cout << ")" << endl; - switch (p->tag) { - case VarPatV: { - address a = allocate_value(copy_val(v, lineno)); - vars.push_back(*p->u.var_pat.name); - return new Env(*p->u.var_pat.name, a, env); - } - case TupleV: - switch (v->tag) { - case TupleV: { - if (p->u.tuple.elts->size() != v->u.tuple.elts->size()) { - cerr << "runtime error: arity mismatch in tuple pattern match" << endl; - exit(-1); - } - for (auto i = p->u.tuple.elts->begin(); i != p->u.tuple.elts->end(); - ++i) { - address a = find_field(i->first, v->u.tuple.elts); - env = pattern_match(state->heap[i->second], state->heap[a], - env, vars, lineno); - } - return env; - } - default: - cerr << "internal error, expected a tuple value in pattern, not "; - print_value(v, cerr); - cerr << endl; - exit(-1); - } - case AltV: - switch (v->tag) { - case AltV: { - if (*p->u.alt.choice_name != *v->u.alt.choice_name - || *p->u.alt.alt_name != *v->u.alt.alt_name) - return 0; - env = pattern_match(p->u.alt.arg, v->u.alt.arg, env, vars, lineno); - return env; - } - default: - cerr << "internal error, expected a choice alternative in pattern, not "; - print_value(v, cerr); - cerr << endl; - exit(-1); - } - case FunctionTV: - switch (v->tag) { - case FunctionTV: - env = pattern_match(p->u.fun_type.param, v->u.fun_type.param, env, vars, - lineno); - env = pattern_match(p->u.fun_type.ret, v->u.fun_type.ret, env, vars, - lineno); - return env; - default: - return 0; - } - default: - if (value_equal(p, v, lineno)) - return env; - else - return 0; - } -} - -void pattern_assignment(Value* pat, Value* val, int lineno) { - switch (pat->tag) { - case PtrV: - state->heap[val_to_ptr(pat, lineno)] = val; - break; - case TupleV: { - switch (val->tag) { - case TupleV: { - if (pat->u.tuple.elts->size() != val->u.tuple.elts->size()) { - cerr << "runtime error: arity mismatch in tuple pattern match" << endl; - exit(-1); - } - for (auto i = pat->u.tuple.elts->begin(); i != pat->u.tuple.elts->end(); - ++i) { - address a = find_field(i->first, val->u.tuple.elts); - pattern_assignment(state->heap[i->second], state->heap[a], lineno); - } - break; - } - default: - cerr << "internal error, expected a tuple value on right-hand-side, not "; - print_value(val, cerr); - cerr << endl; - exit(-1); - } - break; - } - case AltV: { - switch (val->tag) { - case AltV: { - if (*pat->u.alt.choice_name != *val->u.alt.choice_name - || *pat->u.alt.alt_name != *val->u.alt.alt_name) { - cerr << "internal error in pattern assignment" << endl; - exit(-1); - } - pattern_assignment(pat->u.alt.arg, val->u.alt.arg, lineno); - break; - } - default: - cerr << "internal error, expected an alternative in left-hand-side, not "; - print_value(val, cerr); - cerr << endl; - exit(-1); - } - break; - } - default: - if (! value_equal(pat, val, lineno)) { - cerr << "internal error in pattern assignment" << endl; - exit(-1); - } - } -} - - -/***** state transitions for lvalues *****/ - -void step_lvalue() { - Frame* frame = state->stack->curr; - Action* act = frame->todo->curr; - Expression* exp = act->u.exp; - cout << "--- step lvalue "; print_exp(exp); cout << " --->" << endl; - switch (exp->tag) { - case Variable: { - // { {x :: C, E, F} :: S, H} - // -> { {E(x) :: C, E, F} :: S, H} - address a = lookup(exp->lineno, current_env(state), *(exp->u.variable.name), - print_error_string); - Value* v = make_ptr_val(a); - check_alive(v, exp->lineno); - frame->todo = cons(make_val_act(v), frame->todo->next); - break; - } - case GetField: { - // { {e.f :: C, E, F} :: S, H} - // -> { e :: [].f :: C, E, F} :: S, H} - frame->todo = cons(make_lval_act(exp->u.get_field.aggregate), frame->todo); - act->pos++; - break; - } - case Index: { - // { {e[i] :: C, E, F} :: S, H} - // -> { e :: [][i] :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(exp->u.index.aggregate), frame->todo); - act->pos++; - break; - } - case Tuple: { - // { {(f1=e1,...) :: C, E, F} :: S, H} - // -> { {e1 :: (f1=[],...) :: C, E, F} :: S, H} - Expression* e1 = (*exp->u.tuple.fields)[0].second; - frame->todo = cons(make_lval_act(e1), frame->todo); - act->pos++; - break; - } - case Integer: case Boolean: case Call: case PrimitiveOp: - case IntT: case BoolT: case TypeT: case FunctionT: case AutoT: - case PatternVariable: { - frame->todo = cons(make_exp_act(exp), - cons(make_exp_to_lval_act(), - frame->todo->next)); - } - } -} - -/***** state transitions for expressions *****/ - -void step_exp() { - Frame* frame = state->stack->curr; - Action* act = frame->todo->curr; - Expression* exp = act->u.exp; - cout << "--- step exp "; print_exp(exp); cout << " --->" << endl; - switch (exp->tag) { - case PatternVariable: { - frame->todo = cons(make_exp_act(exp->u.pattern_variable.type), frame->todo); - act->pos++; - break; - } - case Index: { - // { { e[i] :: C, E, F} :: S, H} - // -> { { e :: [][i] :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(exp->u.index.aggregate), frame->todo); - act->pos++; - break; - } - case Tuple: { - if (exp->u.tuple.fields->size() > 0) { - // { {(f1=e1,...) :: C, E, F} :: S, H} - // -> { {e1 :: (f1=[],...) :: C, E, F} :: S, H} - Expression* e1 = (*exp->u.tuple.fields)[0].second; - frame->todo = cons(make_exp_act(e1), frame->todo); - act->pos++; - } else { - create_tuple(frame, act, exp); - } - break; - } - case GetField: { - // { { e.f :: C, E, F} :: S, H} - // -> { { e :: [].f :: C, E, F} :: S, H} - frame->todo = cons(make_lval_act(exp->u.get_field.aggregate), frame->todo); - act->pos++; - break; - } - case Variable: { - // { {x :: C, E, F} :: S, H} -> { {H(E(x)) :: C, E, F} :: S, H} - address a = lookup(exp->lineno, current_env(state), *(exp->u.variable.name), - print_error_string); - Value* v = state->heap[a]; - frame->todo = cons(make_val_act(v), frame->todo->next); - break; - } - case Integer: - // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - frame->todo = cons(make_val_act(make_int_val(exp->u.integer)), - frame->todo->next); - break; - case Boolean: - // { {n :: C, E, F} :: S, H} -> { {n' :: C, E, F} :: S, H} - frame->todo = cons(make_val_act(make_bool_val(exp->u.boolean)), - frame->todo->next); - break; - case PrimitiveOp: - if (exp->u.primitive_op.arguments->size() > 0) { - // { {op(e :: es) :: C, E, F} :: S, H} - // -> { e :: op([] :: es) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(exp->u.primitive_op.arguments->front()), - frame->todo); - act->pos++; - } else { - // { {v :: op(]) :: C, E, F} :: S, H} - // -> { {eval_prim(op, ()) :: C, E, F} :: S, H} - Value* v = eval_prim(exp->u.primitive_op.operator_, act->results, - exp->lineno); - frame->todo = cons(make_val_act(v), frame->todo->next->next); - } - break; - case Call: - // { {e1(e2) :: C, E, F} :: S, H} - // -> { {e1 :: [](e2) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(exp->u.call.function), frame->todo); - act->pos++; - break; - case IntT: { - Value* v = make_int_type_val(); - frame->todo = cons(make_val_act(v), frame->todo->next); - break; - } - case BoolT: { - Value* v = make_bool_type_val(); - frame->todo = cons(make_val_act(v), frame->todo->next); - break; - } - case AutoT: { - Value* v = make_auto_type_val(); - frame->todo = cons(make_val_act(v), frame->todo->next); - break; - } - case TypeT: { - Value* v = make_type_type_val(); - frame->todo = cons(make_val_act(v), frame->todo->next); - break; - } - case FunctionT: { - frame->todo = cons(make_exp_act(exp->u.function_type.parameter), - frame->todo); - act->pos++; - break; - } - } // switch (exp->tag) -} - -/***** state transitions for statements *****/ - -bool is_while_act(Action* act) { - switch (act->tag) { - case StatementAction: - switch (act->u.stmt->tag) { - case While: - return true; - default: - return false; - } - default: - return false; - } -} - -bool is_block_act(Action* act) { - switch (act->tag) { - case StatementAction: - switch (act->u.stmt->tag) { - case Block: - return true; - default: - return false; - } - default: - return false; - } -} - -void step_stmt() { - Frame* frame = state->stack->curr; - Action* act = frame->todo->curr; - Statement* stmt = act->u.stmt; - cout << "--- step stmt "; print_stmt(stmt, 1); cout << " --->" << endl; - switch (stmt->tag) { - case Match: - // { { (match (e) ...) :: C, E, F} :: S, H} - // -> { { e :: (match ([]) ...) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.match_stmt.exp), frame->todo); - act->pos++; - break; - case While: - // { { (while (e) s) :: C, E, F} :: S, H} - // -> { { e :: (while ([]) s) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.while_stmt.cond), frame->todo); - act->pos++; - break; - case Break: - // { { break; :: ... :: (while (e) s) :: C, E, F} :: S, H} - // -> { { C, E', F} :: S, H} - frame->todo = frame->todo->next; - while (frame->todo && ! is_while_act(frame->todo->curr)) { - if (is_block_act(frame->todo->curr)) { - kill_scope(stmt->lineno, frame->scopes->curr); - frame->scopes = frame->scopes->next; - } - frame->todo = frame->todo->next; - } - frame->todo = frame->todo->next; - break; - case Continue: - // { { continue; :: ... :: (while (e) s) :: C, E, F} :: S, H} - // -> { { (while (e) s) :: C, E', F} :: S, H} - frame->todo = frame->todo->next; - while (frame->todo && ! is_while_act(frame->todo->curr)) { - if (is_block_act(frame->todo->curr)) { - kill_scope(stmt->lineno, frame->scopes->curr); - frame->scopes = frame->scopes->next; - } - frame->todo = frame->todo->next; - } - break; - case Block: { - if (act->pos == -1) { - Scope* scope = new Scope(current_env(state), list()); - frame->scopes = cons(scope, frame->scopes); - frame->todo = cons(make_stmt_act(stmt->u.block.stmt), - frame->todo); - act->pos++; - } else { - Scope* scope = frame->scopes->curr; - kill_scope(stmt->lineno, scope); - frame->scopes = frame->scopes->next; - frame->todo = frame->todo->next; - } - break; - } - case VariableDefinition: - // { {(var x = e) :: C, E, F} :: S, H} - // -> { {e :: (var x = []) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.variable_definition.init), - frame->todo); - act->pos++; - break; - case ExpressionStatement: - // { {e :: C, E, F} :: S, H} - // -> { {e :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.exp), frame->todo); - break; - case Assign: - // { {(lv = e) :: C, E, F} :: S, H} - // -> { {lv :: ([] = e) :: C, E, F} :: S, H} - frame->todo = cons(make_lval_act(stmt->u.assign.lhs), - frame->todo); - act->pos++; - break; - case If: - // { {(if (e) thn else els) :: C, E, F} :: S, H} - // -> { { e :: (if ([]) thn else els) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.if_stmt.cond), frame->todo); - act->pos++; - break; - case Return: - // { {return e :: C, E, F} :: S, H} - // -> { {e :: return [] :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.return_stmt), frame->todo); - act->pos++; - break; - case Sequence: - // { { (s1,s2) :: C, E, F} :: S, H} - // -> { { s1 :: s2 :: C, E, F} :: S, H} - Cons* todo = frame->todo->next; - if (stmt->u.sequence.next) { - todo = cons(make_stmt_act(stmt->u.sequence.next), todo); - } - frame->todo = cons(make_stmt_act(stmt->u.sequence.stmt), todo); - break; - } -} - -address get_member(address a, string f) { - vector >* fields; - Value* v = state->heap[a]; - switch (v->tag) { - case StructV: - fields = v->u.struct_val.inits->u.tuple.elts; - try { - return find_field(f, fields); - } catch (std::domain_error de) { - cerr << "runtime error, member " << f << " not in "; - print_value(v, cerr); cerr << endl; - exit(-1); - } - break; - case TupleV: - fields = v->u.tuple.elts; - try { - return find_field(f, fields); - } catch (std::domain_error de) { - cerr << "field " << f << " not in "; - print_value(v, cerr); cerr << endl; - exit(-1); - } - break; - case ChoiceTV: { - try { - find_alist(f, v->u.choice_type.alternatives); - auto ac = make_alt_cons(f, * v->u.choice_type.name); - return allocate_value(ac); - } catch (std::domain_error de) { - cerr << "alternative " << f << " not in "; - print_value(v, cerr); cerr << endl; - exit(-1); - } - break; - } - default: - cerr << "field access not allowed for value "; - print_value(v, cerr); - cerr << endl; - exit(-1); - } -} - -Cons* insert_delete(Action* del, Cons* todo) { - if (todo) { - switch (todo->curr->tag) { - case StatementAction: { - // This places the delete before the enclosing statement. - // Not sure if that is OK. Conceptually it should go after - // but that is tricky for some statements, like 'return'. -Jeremy - return cons(del, todo); - } - case LValAction: case ExpressionAction: case ValAction: - case ExpToLValAction: case DeleteTmpAction: - return cons(todo->curr, insert_delete(del, todo->next)); - } - } else { - return cons(del, todo); - } -} - -/***** State transition for handling a value *****/ - -void handle_value() { - Frame* frame = state->stack->curr; - Action* val_act = frame->todo->curr; - Action* act = frame->todo->next->curr; - act->results.push_back(val_act->u.val); - act->pos++; - - cout << "--- handle value "; print_value(val_act->u.val, cout); - cout << " with "; print_act(act, cout); cout << " --->" << endl; - - switch (act->tag) { - case DeleteTmpAction: { - kill_value(state->heap[act->u.delete_]); - frame->todo = cons(val_act, frame->todo->next->next); - break; - } - case ExpToLValAction: { - address a = allocate_value(act->results[0]); - auto del = make_delete_act(a); - frame->todo = cons(make_val_act(make_ptr_val(a)), - insert_delete(del, frame->todo->next->next)); - break; - } - case LValAction: { - Expression* exp = act->u.exp; - switch (exp->tag) { - case GetField: { - // { v :: [].f :: C, E, F} :: S, H} - // -> { { &v.f :: C, E, F} :: S, H } - Value* str = act->results[0]; - try { - address a = get_member(val_to_ptr(str, exp->lineno), - * exp->u.get_field.field); - frame->todo = cons(make_val_act(make_ptr_val(a)), - frame->todo->next->next); - } catch (std::domain_error de) { - cerr << "field " << * exp->u.get_field.field << " not in "; - print_value(str, cerr); - cerr << endl; - exit(-1); - } - break; - } - case Index: { - if (act->pos == 1) { - frame->todo = cons(make_exp_act(exp->u.index.offset), - frame->todo->next); - } else if (act->pos == 2) { - // { v :: [][i] :: C, E, F} :: S, H} - // -> { { &v[i] :: C, E, F} :: S, H } - Value* tuple = act->results[0]; - string f = std::to_string(to_integer(act->results[1])); - try { - address a = find_field(f, tuple->u.tuple.elts); - frame->todo = cons(make_val_act(make_ptr_val(a)), - frame->todo->next->next); - } catch (std::domain_error de) { - cerr << "runtime error: field " << f << "not in "; - print_value(tuple, cerr); - cerr << endl; - exit(-1); - } - } - break; - } - case Tuple: { - if (act->pos != exp->u.tuple.fields->size()) { - // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, H} - // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, H} - Expression* elt = (*exp->u.tuple.fields)[act->pos].second; - frame->todo = cons(make_lval_act(elt), frame->todo->next); - } else { - frame->todo = frame->todo->next; - create_tuple(frame, act, exp); - } - break; - } - default: - cerr << "internal error in handle_value, LValAction" << endl; - exit(-1); - } - break; - } - case ExpressionAction: { - Expression* exp = act->u.exp; - switch (exp->tag) { - case PatternVariable: { - auto v = make_var_pat_val(* exp->u.pattern_variable.name, - act->results[0]); - frame->todo = cons(make_val_act(v), frame->todo->next->next); - break; - } - case Tuple: { - if (act->pos != exp->u.tuple.fields->size()) { - // { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S, H} - // -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S, H} - Expression* elt = (*exp->u.tuple.fields)[act->pos].second; - frame->todo = cons(make_exp_act(elt), frame->todo->next); - } else { - frame->todo = frame->todo->next; - create_tuple(frame, act, exp); - } - break; - } - case Index: { - if (act->pos == 1) { - frame->todo = cons(make_exp_act(exp->u.index.offset), - frame->todo->next); - } else if (act->pos == 2) { - auto tuple = act->results[0];; - switch (tuple->tag) { - case TupleV: { - // { { v :: [][i] :: C, E, F} :: S, H} - // -> { { v_i :: C, E, F} : S, H} - string f = std::to_string(to_integer(act->results[1])); - try { - auto a = find_field(f, tuple->u.tuple.elts); - frame->todo = cons(make_val_act(state->heap[a]), - frame->todo->next->next); - } catch (std::domain_error de) { - cerr << "runtime error, field " << f - << " not in "; - print_value(tuple, cerr); - cerr << endl; - exit(-1); - } - break; - } - default: - cerr << "runtime type error, expected a tuple in field access, not "; - print_value(tuple, cerr); - exit(-1); - } - } - break; - } - case GetField: { - // { { v :: [].f :: C, E, F} :: S, H} - // -> { { v_f :: C, E, F} : S, H} - auto a = get_member(val_to_ptr(act->results[0], exp->lineno), - * exp->u.get_field.field); - frame->todo = cons(make_val_act(state->heap[a]), - frame->todo->next->next); - break; - } - case PrimitiveOp: { - if (act->pos != exp->u.primitive_op.arguments->size()) { - // { {v :: op(vs,[],e,es) :: C, E, F} :: S, H} - // -> { {e :: op(vs,v,[],es) :: C, E, F} :: S, H} - Expression* arg = (*exp->u.primitive_op.arguments)[act->pos]; - frame->todo = cons(make_exp_act(arg), frame->todo->next); - } else { - // { {v :: op(vs,[]) :: C, E, F} :: S, H} - // -> { {eval_prim(op, (vs,v)) :: C, E, F} :: S, H} - Value* v = eval_prim(exp->u.primitive_op.operator_, act->results, - exp->lineno); - frame->todo = cons(make_val_act(v), frame->todo->next->next); - } - break; - } - case Call: { - if (act->pos == 1) { - // { { v :: [](e) :: C, E, F} :: S, H} - // -> { { e :: v([]) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(exp->u.call.argument), - frame->todo->next); - } else if (act->pos == 2) { - // { { v2 :: v1([]) :: C, E, F} :: S, H} - // -> { {C',E',F'} :: {C, E, F} :: S, H} - frame->todo = frame->todo->next->next; - call_function(exp->lineno, act->results, state); - } else { - cerr << "internal error in handle_value with Call" << endl; - exit(-1); - } - break; - } - case FunctionT: { - if (act->pos == 2) { - // { { rt :: fn pt -> [] :: C, E, F} :: S, H} - // -> { fn pt -> rt :: {C, E, F} :: S, H} - Value* v = make_fun_type_val(act->results[0], act->results[1]); - frame->todo = cons(make_val_act(v), frame->todo->next->next); - } else { - // { { pt :: fn [] -> e :: C, E, F} :: S, H} - // -> { { e :: fn pt -> []) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(exp->u.function_type.return_type), - frame->todo->next); - } - break; - } - case Variable: case Integer: case Boolean: - case IntT: case BoolT: case TypeT: case AutoT: - cerr << "internal error, bad expression context in handle_value" << endl; - exit(-1); - } - break; - } - case StatementAction: { - Statement* stmt = act->u.stmt; - switch (stmt->tag) { - case ExpressionStatement: - frame->todo = frame->todo->next->next; - break; - case VariableDefinition: { - if (act->pos == 1) { - frame->todo = cons(make_exp_act(stmt->u.variable_definition.pat), - frame->todo->next); - } else if (act->pos == 2) { - // { { v :: (x = []) :: C, E, F} :: S, H} - // -> { { C, E(x := a), F} :: S, H(a := copy(v))} - Value* v = act->results[0]; - Value* p = act->results[1]; - //address a = allocate_value(copy_val(v)); - frame->scopes->curr->env = - pattern_match(p, v, frame->scopes->curr->env, - frame->scopes->curr->locals, stmt->lineno); - if (!frame->scopes->curr->env) { - cerr << stmt->lineno - << ": internal error in variable definition, match failed" - << endl; - exit(-1); - } - frame->todo = frame->todo->next->next; - } - break; - } - case Assign: - if (act->pos == 1) { - // { { a :: ([] = e) :: C, E, F} :: S, H} - // -> { { e :: (a = []) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(stmt->u.assign.rhs), - frame->todo->next); - } else if (act->pos == 2) { - // { { v :: (a = []) :: C, E, F} :: S, H} - // -> { { C, E, F} :: S, H(a := v)} - auto pat = act->results[0]; - auto val = act->results[1]; - pattern_assignment(pat, val, stmt->lineno); - frame->todo = frame->todo->next->next; - } - break; - case If: - if (val_to_bool(act->results[0], stmt->lineno)) { - // { {true :: if ([]) thn else els :: C, E, F} :: S, H} - // -> { { thn :: C, E, F } :: S, H} - frame->todo = cons(make_stmt_act(stmt->u.if_stmt.thn), - frame->todo->next->next); - } else { - // { {false :: if ([]) thn else els :: C, E, F} :: S, H} - // -> { { els :: C, E, F } :: S, H} - frame->todo = cons(make_stmt_act(stmt->u.if_stmt.els), - frame->todo->next->next); - } - break; - case While: - if (val_to_bool(act->results[0], stmt->lineno)) { - // { {true :: (while ([]) s) :: C, E, F} :: S, H} - // -> { { s :: (while (e) s) :: C, E, F } :: S, H} - frame->todo->next->curr->pos = -1; - frame->todo->next->curr->results.clear(); - frame->todo = cons(make_stmt_act(stmt->u.while_stmt.body), - frame->todo->next); - } else { - // { {false :: (while ([]) s) :: C, E, F} :: S, H} - // -> { { C, E, F } :: S, H} - frame->todo->next->curr->pos = -1; - frame->todo->next->curr->results.clear(); - frame->todo = frame->todo->next->next; - } - break; - case Match: { - /* - Regarding act->pos: - * odd: start interpretting the pattern of a clause - * even: finished interpretting the pattern, now try to match - - Regarding act->results: - * 0: the value that we're matching - * 1: the pattern for clause 0 - * 2: the pattern for clause 1 - * ... - */ - auto clause_num = (act->pos - 1) / 2; - if (clause_num >= stmt->u.match_stmt.clauses->size()) { - frame->todo = frame->todo->next->next; - break; - } - auto c = stmt->u.match_stmt.clauses->begin(); - std::advance(c, clause_num); - - if (act->pos % 2 == 1) { - // start interpreting the pattern of the clause - // { {v :: (match ([]) ...) :: C, E, F} :: S, H} - // -> { {pi :: (match ([]) ...) :: C, E, F} :: S, H} - frame->todo = cons(make_exp_act(c->first), frame->todo->next); - } else { // try to match - auto v = act->results[0]; - auto pat = act->results[clause_num + 1]; - auto env = current_env(state); - list vars; - Env* new_env = pattern_match(pat, v, env, vars, stmt->lineno); - if (new_env) { // we have a match, start the body - Scope* new_scope = new Scope(new_env, vars); - frame->scopes = cons(new_scope, frame->scopes); - Statement* body_block = make_block(stmt->lineno, c->second); - Action* body_act = make_stmt_act(body_block); - body_act->pos = 0; - frame->todo = cons(make_stmt_act(c->second), - cons(body_act, frame->todo->next->next)); - } else { - act->pos++; - clause_num = (act->pos - 1) / 2; - if (clause_num < stmt->u.match_stmt.clauses->size()) { - // move on to the next clause - c = stmt->u.match_stmt.clauses->begin(); - std::advance(c, clause_num); - frame->todo = cons(make_exp_act(c->first), frame->todo->next); - } else { // No more clauses in match - frame->todo = frame->todo->next->next; - } - } - } - break; - } - case Return: { - // { {v :: return [] :: C, E, F} :: {C', E', F'} :: S, H} - // -> { {v :: C', E', F'} :: S, H} - Value* ret_val = copy_val(val_act->u.val, stmt->lineno); - kill_locals(stmt->lineno, frame); - state->stack = state->stack->next; - frame = state->stack->curr; - frame->todo = cons(make_val_act(ret_val), frame->todo); - break; - } - case Block: - case Sequence: - case Break: - case Continue: - cerr << "internal error in handle_value, unhandled statement "; - print_stmt(stmt, 1); - cerr << endl; - exit(-1); - } // switch stmt - break; - } - case ValAction: - cerr << "internal error, ValAction in handle_value" << endl; - exit(-1); - } // switch act -} - -/***** state transition *****/ - -void step() { - Frame* frame = state->stack->curr; - if (! frame->todo) { - cerr << "runtime error: fell off end of function " << frame->name - << " without `return`" << endl; - exit(-1); - } - - Action* act = frame->todo->curr; - switch (act->tag) { - case DeleteTmpAction: - cerr << "internal error in step, did not expect DeleteTmpAction" << endl; - break; - case ExpToLValAction: - cerr << "internal error in step, did not expect ExpToLValAction" << endl; - break; - case ValAction: - handle_value(); - break; - case LValAction: - step_lvalue(); - break; - case ExpressionAction: - step_exp(); - break; - case StatementAction: - step_stmt(); - break; - } // switch -} - -/***** interpret the whole program *****/ - -int interp_program(list* fs) { - state = new State(); // runtime state - cout << "********** initializing globals **********" << endl; - init_globals(fs); - - Expression* arg = make_tuple(0, new vector >()); - Expression* call_main = make_call(0, make_var(0, "main"), arg); - Cons* todo = cons(make_exp_act(call_main), (Cons*)0); - Scope* scope = new Scope(globals, list()); - Frame* frame = new Frame("top", cons(scope, (Cons*)0), todo); - state->stack = cons(frame, (Cons*)0); - - cout << "********** calling main function **********" << endl; - print_state(cout); - - while (length(state->stack) > 1 - || length(state->stack->curr->todo) > 1 - || state->stack->curr->todo->curr->tag != ValAction) { - step(); - print_state(cout); - } - Value* v = state->stack->curr->todo->curr->u.val; - return val_to_int(v, 0); -} - -/***** interpret an expression (at compile-time) *****/ - -Value* interp_exp(Env* env, Expression* e) { - Cons* todo = cons(make_exp_act(e), (Cons*)0); - Scope* scope = new Scope(env, list()); - Frame* frame = new Frame("interp_exp", cons(scope, (Cons*)0), todo); - state->stack = cons(frame, (Cons*)0); - - while (length(state->stack) > 1 - || length(state->stack->curr->todo) > 1 - || state->stack->curr->todo->curr->tag != ValAction) { - step(); - } - Value* v = state->stack->curr->todo->curr->u.val; - return v; -} diff --git a/executable-semantics/interp.h b/executable-semantics/interp.h deleted file mode 100644 index 09606a7848030..0000000000000 --- a/executable-semantics/interp.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef INTERP_H -#define INTERP_H - -#include "ast.h" -#include "assoc_list.h" -#include "cons_list.h" -#include -#include -using std::vector; -using std::list; - -struct Value; - -typedef unsigned int address; -typedef list< pair > VarValues; -typedef AList Env; - -/***** Values *****/ - -enum ValKind { IntV, FunV, PtrV, BoolV, StructV, AltV, TupleV, - VarTV, IntTV, BoolTV, TypeTV, FunctionTV, PointerTV, AutoTV, - TupleTV, StructTV, ChoiceTV, VarPatV, - AltConsV }; - -struct Value { - ValKind tag; - bool alive; - union { - int integer; - bool boolean; - struct { string* name; - Value* param; - Statement* body; - } fun; - struct { Value* type; Value* inits; } struct_val; - struct { string* alt_name; string* choice_name; } alt_cons; - struct { string* alt_name; string* choice_name; Value* arg; } alt; - struct { vector >* elts; } tuple; - address ptr; - string* var_type; - struct { string* name; Value* type; } var_pat; - struct { Value* param; Value* ret; } fun_type; - struct { Value* type; } ptr_type; - struct { string* name; VarValues* fields; VarValues* methods; } struct_type; - struct { string* name; VarValues* fields; } tuple_type; - struct { string* name; VarValues* alternatives; } choice_type; - struct { list* params; Value* type; } implicit; - } u; -}; - -Value* make_int_val(int i); -Value* make_bool_val(bool b); -Value* make_fun_val(string name, VarValues* implicit_params, Value* param, - Statement* body, vector* implicit_args); -Value* make_ptr_val(address addr); -Value* make_struct_val(Value* type, vector >* inits); -Value* make_tuple_val(vector >* elts); -Value* make_alt_val(string alt_name, string choice_name, Value* arg); -Value* make_alt_cons(string alt_name, string choice_name); - -Value* make_var_pat_val(string name, Value* type); - -Value* make_var_type_val(string name); -Value* make_int_type_val(); -Value* make_auto_type_val(); -Value* make_bool_type_val(); -Value* make_type_type_val(); -Value* make_fun_type_val(Value* param, Value* ret); -Value* make_ptr_type_val(Value* type); -Value* make_struct_type_val(string name, VarValues* fields, VarValues* methods); -Value* make_tuple_type_val(VarValues* fields); -Value* make_void_type_val(); -Value* make_choice_type_val(string* name, VarValues* alts); - -bool value_equal(Value* v1, Value* v2, int lineno); - -/***** Actions *****/ - -enum ActionKind { LValAction, ExpressionAction, StatementAction, ValAction, - ExpToLValAction, DeleteTmpAction }; - -struct Action { - ActionKind tag; - union { - Expression* exp; // for LValAction and ExpressionAction - Statement* stmt; - Value* val; // for finished actions with a value (ValAction) - address delete_; - } u; - int pos; // position or state of the action - vector results; // results from subexpression -}; -typedef Cons* ActionList; - -/***** Scopes *****/ - -struct Scope { - Scope(Env* e, const list& l) : env(e), locals(l) { } - Env* env; - list locals; -}; - -/***** Frames and State *****/ - -struct Frame { - Cons* todo; - Cons* scopes; - string name; - Frame(string n, Cons* s, Cons* c) - : name(n), scopes(s), todo(c) { } -}; - -struct State { - Cons* stack; - vector heap; -}; - -extern State* state; - -void print_value(Value* val, std::ostream& out); -void print_env(Env* env); -address allocate_value(Value* v); -Value* copy_val(Value* val, int lineno); -int to_integer(Value* v); - -/***** Interpreters *****/ - -int interp_program(list* fs); -Value* interp_exp(Env* env, Expression* e); - -#endif diff --git a/executable-semantics/syntax.l b/executable-semantics/syntax.l deleted file mode 100644 index 7622824d18dfd..0000000000000 --- a/executable-semantics/syntax.l +++ /dev/null @@ -1,77 +0,0 @@ -%{ -#include -#include "ast.h" -#include "syntax.tab.h" -%} -%option yylineno - -integer_literal [0-9]+ -identifier [A-Za-z_][A-Za-z0-9_]* -AND "and" -OR "or" -NOT "not" -EQUAL "==" -ARROW "->" -COMMENT \/\/[^\n]*\n -RETURN "return" -IF "if" -ELSE "else" -WHILE "while" -BREAK "break" -CONTINUE "continue" -INT "Int" -BOOL "Bool" -TYPE "Type" -FN "fn" -FNTY "fnty" -TUPLETY "Tuple" -TRUE "true" -FALSE "false" -VAR "var" -STRUCT "struct" -CHOICE "choice" -MATCH "match" -CASE "case" -DBLARROW "=>" -DEFAULT "default" -AUTO "auto" -%% -{STRUCT} { return STRUCT; } -{CHOICE} { return CHOICE; } -{MATCH} { return MATCH; } -{CASE} { return CASE; } -{DBLARROW} { return DBLARROW; } -{DEFAULT} { return DEFAULT; } -{AUTO} { return AUTO; } -{AND} { return AND; } -{OR} { return OR; } -{NOT} { return NOT; } -{IF} { return IF; } -{ELSE} { return ELSE; } -{WHILE} { return WHILE; } -{BREAK} { return BREAK; } -{CONTINUE} { return CONTINUE; } -{INT} { return INT; } -{BOOL} { return BOOL; } -{TYPE} { return TYPE; } -{FN} { return FN; } -{FNTY} { return FNTY; } -{ARROW} { return ARROW; } -{VAR} { return VAR; } -{EQUAL} { return EQUAL; } -{RETURN} { return RETURN; } -{TRUE} { return TRUE; } -{FALSE} { return FALSE; } -{identifier} { - int n = strlen(yytext); - yylval.str = (char*)malloc((n + 1) * sizeof(char)); - strncpy(yylval.str, yytext, n + 1); - return identifier; - } -{integer_literal} {yylval.num = atof(yytext); return integer_literal;} -[ \t\n]+ ; -{COMMENT} ; -. {return yytext[0];} -%% -int yywrap() {return 1;} - diff --git a/executable-semantics/syntax.y b/executable-semantics/syntax.y deleted file mode 100644 index 7a1a5f4ac302e..0000000000000 --- a/executable-semantics/syntax.y +++ /dev/null @@ -1,336 +0,0 @@ -%{ -#include -#include -#include -#include -#include -#include "ast.h" -#include "typecheck.h" -#include "interp.h" - -extern FILE* yyin; -extern int yylineno; -char* input_filename; - -void yyerror(char*s) { - fprintf(stderr, "%s:%d: %s\n", input_filename, yylineno, s); - exit(-1); -} -// void yyerror(char *s, ...); - -extern int yylex(); -extern int yywrap(); - -//#include "typecheck.h" -//#include "eval.h" -#include -#include "ast.h" -using std::list; -using std::pair; -using std::make_pair; -using std::cout; -using std::endl; - -static list program; -%} -%union - { - char* str; - int num; - Expression* expression; - VarTypes* field_types; - Statement* statement; - Statement* statement_list; - struct FunctionDefinition* function_definition; - Declaration* declaration; - list* declaration_list; - Member* member; - list* member_list; - ExpOrFieldList* field_list; - pair* alternative; - list >* alternative_list; - pair* clause; - list< pair >* clause_list; - Expression* fun_type; -}; - -%token integer_literal -%token identifier -%type designator -%type declaration -%type function_declaration -%type function_definition -%type declaration_list -%type statement -%type statement_list -%type expression -%type pattern -%type return_type -%type tuple -%type member -%type member_list -%type field -%type field_list -%type alternative -%type alternative_list -%type clause -%type clause_list -%token AND -%token OR -%token NOT -%token INT -%token BOOL -%token TYPE -%token FN -%token FNTY -%token ARROW -%token PTR -%token VAR -%token DIV -%token EQUAL -%token IF -%token ELSE -%token WHILE -%token BREAK -%token CONTINUE -%token DELETE -%token RETURN -%token TRUE -%token FALSE -%token NEW -%token STRUCT -%token CHOICE -%token MATCH -%token CASE -%token DBLARROW -%token DEFAULT -%token AUTO -%nonassoc '{' '}' -%nonassoc ':' ',' DBLARROW -%left OR AND -%nonassoc EQUAL NOT -%left '+' '-' -%left '.' ARROW -%nonassoc '(' ')' '[' ']' -%start input -%locations -%% -input: declaration_list - { - printf("********** source program **********\n"); - print_list($1, print_decl, ""); - printf("********** type checking **********\n"); - state = new State(); // compile-time state - pair p = top_level($1); - TypeEnv* top = p.first; - Env* ct_top = p.second; - list new_decls; - for (auto i = $1->begin(); i != $1->end(); ++i) { - new_decls.push_back(typecheck_decl(*i, top, ct_top)); - } - printf("\n"); - printf("********** type checking complete **********\n"); - print_list(&new_decls, print_decl, ""); - printf("********** starting execution **********\n"); - int result = interp_program(&new_decls); - cout << "result: " << result << endl; - } -; -pattern: - expression - { $$ = $1 } -; -expression: - identifier - { $$ = make_var(yylineno, $1); } -| expression designator - { $$ = make_get_field(yylineno, $1, $2); } -| expression '[' expression ']' - { $$ = make_index(yylineno, $1, $3); } -| expression ':' identifier - { $$ = make_var_pat(yylineno, $3, $1); } -| integer_literal - { $$ = make_int(yylineno, $1); } -| TRUE - { $$ = make_bool(yylineno, true); } -| FALSE - { $$ = make_bool(yylineno, false); } -| INT - { $$ = make_int_type(yylineno); } -| BOOL - { $$ = make_bool_type(yylineno); } -| TYPE - { $$ = make_type_type(yylineno); } -| AUTO - { $$ = make_auto_type(yylineno); } -| tuple { $$ = $1; } -| expression EQUAL expression - { $$ = make_binop(yylineno, Eq, $1, $3); } -| expression '+' expression - { $$ = make_binop(yylineno, Add, $1, $3); } -| expression '-' expression - { $$ = make_binop(yylineno, Sub, $1, $3); } -| expression AND expression - { $$ = make_binop(yylineno, And, $1, $3); } -| expression OR expression - { $$ = make_binop(yylineno, Or, $1, $3); } -| NOT expression - { $$ = make_unop(yylineno, Not, $2); } -| '-' expression - { $$ = make_unop(yylineno, Neg, $2); } -| expression tuple - { - if ($2->tag == Tuple) { - $$ = make_call(yylineno, $1, $2); - } else { - auto vec = new vector >(); - vec->push_back(make_pair("", $2)); - $$ = make_call(yylineno, $1, make_tuple(yylineno, vec)); - } - } -| FNTY tuple return_type - { $$ = make_fun_type(yylineno, $2, $3); } -; -designator: '.' identifier { $$ = $2; } -; -tuple: '(' field_list ')' - { - switch ($2->tag) { - case Exp: - $$ = $2->u.exp; - break; - case FieldList: - auto vec = new vector >($2->u.fields->begin(), - $2->u.fields->end()); - $$ = make_tuple(yylineno, vec); - break; - } - } -; -field: - pattern - { $$ = make_exp($1); } -| designator '=' pattern - { auto fields = new list >(); - fields->push_back(make_pair($1, $3)); - $$ = make_field_list(fields); } -; -field_list: - /* empty */ - { $$ = make_field_list(new list >()); } -| field - { $$ = $1; } -| field ',' field_list - { $$ = cons_field($1, $3); } -; -clause: - CASE pattern DBLARROW statement - { $$ = new pair($2, $4); } -| DEFAULT DBLARROW statement - { - auto vp = make_var_pat(yylineno, "_", make_auto_type(yylineno)); - $$ = new pair(vp, $3); - } -; -clause_list: - /* empty */ - { $$ = new list< pair >(); } -| clause clause_list - { $$ = $2; $$->push_front(*$1); } -; -statement: - expression '=' expression ';' - { $$ = make_assign(yylineno, $1, $3); } -| VAR pattern '=' expression ';' - { $$ = make_var_def(yylineno, $2, $4); } -| expression ';' - { $$ = make_exp_stmt(yylineno, $1); } -| IF '(' expression ')' statement ELSE statement - { $$ = make_if(yylineno, $3, $5, $7); } -| WHILE '(' expression ')' statement - { $$ = make_while(yylineno, $3, $5); } -| BREAK ';' - { $$ = make_break(yylineno); } -| CONTINUE ';' - { $$ = make_continue(yylineno); } -| RETURN expression ';' - { $$ = make_return(yylineno, $2); } -| '{' statement_list '}' - { $$ = make_block(yylineno, $2); } -| MATCH '(' expression ')' '{' clause_list '}' - { $$ = make_match(yylineno, $3, $6); } -; -statement_list: - /* empty */ - { $$ = 0; } -| statement statement_list - { $$ = make_seq(yylineno, $1, $2); } -; -return_type: - /* empty */ - { $$ = make_tuple(yylineno, new vector >()); } -| ARROW expression - { $$ = $2; } -; -function_definition: - FN identifier tuple return_type '{' statement_list '}' - { $$ = make_fun_def(yylineno, $2, $4, $3, $6); } -| FN identifier tuple DBLARROW expression ';' - { $$ = make_fun_def(yylineno, $2, make_auto_type(yylineno), $3, - make_return(yylineno, $5)); } -; -function_declaration: - FN identifier tuple return_type ';' - { $$ = make_fun_def(yylineno, $2, $4, $3, 0); } -; -member: - VAR expression ':' identifier ';' - { $$ = make_field(yylineno, $4, $2); } -; -member_list: - /* empty */ - { $$ = new list(); } -| member member_list - { $$ = $2; $$->push_front($1); } -; -alternative: - identifier tuple ';' - { $$ = new pair($1, $2); } -; -alternative_list: - /* empty */ - { $$ = new list >(); } -| alternative alternative_list - { $$ = $2; $$->push_front(*$1); } -; -declaration: - function_definition - { $$ = make_fun_decl($1); } -| function_declaration - { $$ = make_fun_decl($1); } -| STRUCT identifier '{' member_list '}' - { $$ = make_struct_decl(yylineno, $2, $4); } -| CHOICE identifier '{' alternative_list '}' - { $$ = make_choice_decl(yylineno, $2, $4); } -; -declaration_list: - /* empty */ - { $$ = new list(); } -| declaration declaration_list - { $$ = $2; $$->push_front($1); } -; -%% -int main(int argc, char* argv[]) { - /*yydebug = 1;*/ - - if (argc > 1) { - input_filename = argv[1]; - yyin = fopen(argv[1], "r"); - } - if (argc > 2) { - FILE* program = fopen(argv[2], "r"); - input = read_file(program); - } - yyparse(); - return 0; -} diff --git a/executable-semantics/typecheck.cc b/executable-semantics/typecheck.cc deleted file mode 100644 index 724fd3d5ce53f..0000000000000 --- a/executable-semantics/typecheck.cc +++ /dev/null @@ -1,862 +0,0 @@ -#include "typecheck.h" -#include "interp.h" -#include "cons_list.h" -#include -#include -#include -#include -using std::vector; -using std::set; -using std::map; -using std::cerr; -using std::cout; -using std::endl; -using std::make_pair; - -template -bool list_equal(list* ts1, list* ts2, bool(*eq)(T*,T*)) { - if (ts1->size() == ts2->size()) { - auto iter2 = ts2->begin(); - for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1, ++iter2) { - if (! eq(*iter1, *iter2)) - return false; - } - return true; - } else { - return false; - } -} - -template -bool vector_equal(vector* ts1, vector* ts2, bool(*eq)(T*,T*)) { - if (ts1->size() == ts2->size()) { - auto iter2 = ts2->begin(); - for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1, ++iter2) { - if (! eq(*iter1, *iter2)) - return false; - } - return true; - } else { - return false; - } -} - -bool fields_equal(VarValues* ts1, VarValues* ts2) { - if (ts1->size() == ts2->size()) { - for (auto iter1 = ts1->begin(); iter1 != ts1->end(); ++iter1) { - try { - auto t2 = find_alist(iter1->first, ts2); - if (! type_equal(iter1->second, t2)) - return false; - } catch (std::domain_error de) { - return false; - } - } - return true; - } else { - return false; - } -} - -int find(const string& s, Cons* ls, int n) { - if (ls) { - if (ls->curr == s) - return n; - else - return find(s, ls->next, n + 1); - } else { - cerr << "could not find " << s << endl; - exit(-1); - } -} - -bool type_equal( Value* t1, Value* t2) { - return (t1->tag == VarTV && t2->tag == VarTV - && *t1->u.var_type == *t2->u.var_type) - || (t1->tag == IntTV && t2->tag == IntTV) - || (t1->tag == BoolTV && t2->tag == BoolTV) - || (t1->tag == PointerTV && t2->tag == PointerTV - && type_equal(t1->u.ptr_type.type, t2->u.ptr_type.type)) - || (t1->tag == FunctionTV && t2->tag == FunctionTV - && type_equal(t1->u.fun_type.param, t2->u.fun_type.param) - && type_equal(t1->u.fun_type.ret, t2->u.fun_type.ret)) - || (t1->tag == StructTV && t2->tag == StructTV - && *t1->u.struct_type.name == *t2->u.struct_type.name) - || (t1->tag == ChoiceTV && t2->tag == ChoiceTV - && *t1->u.choice_type.name == *t2->u.choice_type.name) - || (t1->tag == TupleTV && t2->tag == TupleTV - && fields_equal(t1->u.tuple_type.fields, - t2->u.tuple_type.fields)); -} - -void expect_type(int lineno, string context, - Value* expected, Value* actual) { - if (! type_equal(expected, actual)) { - cerr << lineno << ": type error in " << context << endl; - cerr << "expected: "; - print_value(expected, cerr); - cerr << endl << "actual: "; - print_value(actual, cerr); - cerr << endl; - exit(-1); - } -} - -void print_error_string(string s) { - cerr << s; -} - -void print_type_env( TypeEnv* env, std::ostream& out) { - if (env) { - out << env->key << ": "; - print_value(env->value, out); - out << ", "; - print_type_env(env->next, out); - } -} - -void match_types(int lineno, Value* param, Value* arg, - map& tyvar_map) { - switch (param->tag) { - case TupleTV: - if (param->u.tuple_type.fields->size() != arg->u.tuple_type.fields->size()){ - expect_type(lineno, "arity mismatch", param, arg); - exit(-1); - } - for (auto i = param->u.tuple_type.fields->begin(); - i != param->u.tuple_type.fields->end(); - ++i) { - try { - auto argT = find_alist(i->first, arg->u.tuple_type.fields); - match_types(lineno, i->second, argT, tyvar_map); - } catch (std::domain_error de) { - cerr << "missing field " << i->first << endl; - exit(-1); - } - } - break; - case ChoiceTV: - expect_type(lineno, "function call", param, arg); - break; - case StructTV: - expect_type(lineno, "function call", param, arg); - break; - case TypeTV: - expect_type(lineno, "function call", param, arg); - break; - case VarTV: - if (tyvar_map.count(*param->u.var_type) == 0) { - tyvar_map[*param->u.var_type] = arg; - } else { - expect_type(lineno, "call to generic function", - tyvar_map[*param->u.var_type], arg); - } - break; - case BoolTV: - expect_type(lineno, "function call", param, arg); - break; - case IntTV: - expect_type(lineno, "function call", param, arg); - break; - case PointerTV: - switch (arg->tag) { - case PointerTV: - match_types(lineno, param->u.ptr_type.type, arg->u.ptr_type.type, - tyvar_map); - break; - default: - cerr << "expected argument to be a pointer" << endl; - expect_type(lineno, "function call", param, arg); - exit(-1); - } - break; - case FunctionTV: - switch (arg->tag) { - case FunctionTV: - match_types(lineno, param->u.fun_type.param, arg->u.fun_type.param, - tyvar_map); - match_types(lineno, param->u.fun_type.ret, arg->u.fun_type.ret, - tyvar_map); - break; - default: - cerr << "expected argument to be a function" << endl; - expect_type(lineno, "function call", param, arg); - exit(-1); - } - break; - default: - cerr << "in match_type, expected a type, not "; - print_value(param, cerr); - cerr << endl; - exit(-1); - } -} - -Value* subst_type(Value* t, map& tyvar_map) { - switch (t->tag) { - case ChoiceTV: - return t; // update when choices get type parameters - case StructTV: - return t; // update when structs get type parameters - case TypeTV: - return t; - case VarTV: - return tyvar_map[* t->u.var_type]; - case BoolTV: - return t; - case IntTV: - return t; - case PointerTV: - return make_ptr_type_val(subst_type(t->u.ptr_type.type, tyvar_map)); - case FunctionTV: { - return make_fun_type_val(subst_type(t->u.fun_type.param, tyvar_map), - subst_type(t->u.fun_type.ret, tyvar_map)); - } - case TupleTV: { - auto fields = new VarValues(); - for (auto i = t->u.tuple_type.fields->begin(); - i != t->u.tuple_type.fields->end(); ++i) { - fields->push_back(make_pair(i->first, - subst_type(i->second, - tyvar_map))); - } - return make_tuple_type_val(fields); - } - default: - cerr << "in subst_type, expected a type " << endl; - exit(-1); - } -} - - -// Convert tuples to tuple types. -Value* to_type(int lineno, Value* val) { - switch (val->tag) { - case TupleV: { - auto fields = new VarValues(); - for (auto i = val->u.tuple.elts->begin(); - i != val->u.tuple.elts->end(); ++i) { - Value* ty = to_type(lineno, state->heap[i->second]); - fields->push_back(make_pair(i->first, ty)); - } - return make_tuple_type_val(fields); - } - case TupleTV: { - auto fields = new VarValues(); - for (auto i = val->u.tuple_type.fields->begin(); - i != val->u.tuple_type.fields->end(); ++i) { - Value* ty = to_type(lineno, i->second); - fields->push_back(make_pair(i->first, ty)); - } - return make_tuple_type_val(fields); - } - case PointerTV: { - return make_ptr_type_val(to_type(lineno, val->u.ptr_type.type)); - } - case FunctionTV: { - return make_fun_type_val - (to_type(lineno, val->u.fun_type.param), - to_type(lineno, val->u.fun_type.ret)); - } - case VarPatV: { - return make_var_pat_val(* val->u.var_pat.name, - to_type(lineno, val->u.var_pat.type)); - } - case ChoiceTV: - case StructTV: - case TypeTV: - case VarTV: - case BoolTV: - case IntTV: - case AutoTV: - return val; - default: - cerr << lineno << ": in to_type, expected a type, not "; - print_value(val, cerr); - cerr << endl; - exit(-1); - } -} - -// Reify type to type expression. -Expression* reify_type(Value* t, int lineno) { - switch (t->tag) { - case VarTV: - return make_var(0, *t->u.var_type); - case IntTV: - return make_int_type(0); - case BoolTV: - return make_bool_type(0); - case TypeTV: - return make_type_type(0); - case FunctionTV: - return make_fun_type(0, reify_type(t->u.fun_type.param, lineno), - reify_type(t->u.fun_type.ret, lineno)); - case TupleTV: { - auto args = new vector >(); - for (auto i = t->u.tuple_type.fields->begin(); - i != t->u.tuple_type.fields->end(); ++i) { - args->push_back(make_pair(i->first, reify_type(i->second, lineno))); - } - return make_tuple(0, args); - } - case StructTV: - return make_var(0, *t->u.struct_type.name); - case ChoiceTV: - return make_var(0, *t->u.choice_type.name); - default: - cerr << lineno << ": expected a type, not "; print_value(t, cerr); - cerr << endl; - exit(-1); - } -} - - -TCResult typecheck_exp(Expression* e, TypeEnv* env, Env* ct_env, - Value* expected, TCContext context) -{ // expected can be null - switch (e->tag) { - case PatternVariable: { - if (context != PatternContext) { - cerr << e->lineno - << ": compilation error, pattern variables are only allowed in pattern context" - << endl; - } - auto t = to_type(e->lineno, - interp_exp(ct_env, e->u.pattern_variable.type)); - if (t->tag == AutoTV) { - if (expected == 0) { - cerr << e->lineno << ": compilation error, auto not allowed here" << endl; - exit(-1); - } else { - t = expected; - } - } - auto new_e = make_var_pat(e->lineno, *e->u.pattern_variable.name, - reify_type(t, e->lineno)); - return TCResult(new_e, t, - new TypeEnv(* e->u.pattern_variable.name, t, env)); - } - case Index: { - auto res = typecheck_exp(e->u.get_field.aggregate, env, ct_env, 0, - ValueContext); - auto t = res.type; - switch (t->tag) { - case TupleTV: { - auto i = to_integer(interp_exp(ct_env, e->u.index.offset)); - string f = std::to_string(i); - try { - auto fieldT = find_alist(f, t->u.tuple_type.fields); - auto new_e = make_index(e->lineno, res.exp, make_int(e->lineno, i)); - return TCResult(new_e, fieldT, res.env); - } catch (std::domain_error de) { - cerr << e->lineno << ": compilation error, field " << f - << " is not in the tuple "; - print_value(t, cerr); - cerr << endl; - } - } - default: - cerr << e->lineno << ": compilation error, expected a tuple" << endl; - exit(-1); - } - } - case Tuple: { - auto new_args = new vector >(); - auto arg_types = new VarValues(); - auto new_env = env; - int i = 0; - for (auto arg = e->u.tuple.fields->begin(); - arg != e->u.tuple.fields->end(); ++arg, ++i) { - Value* arg_expected = 0; - if (expected && expected->tag == TupleTV) { - try { - arg_expected = find_alist(arg->first, expected->u.tuple_type.fields); - } catch (std::domain_error de) { - cerr << e->lineno << ": compilation error, missing field " - << arg->first << endl; - exit(-1); - } - } - auto arg_res = typecheck_exp(arg->second, new_env, ct_env, - arg_expected, context); - new_env = arg_res.env; - new_args->push_back(make_pair(arg->first, arg_res.exp)); - arg_types->push_back(make_pair(arg->first, arg_res.type)); - } - auto tupleE = make_tuple(e->lineno, new_args); - auto tupleT = make_tuple_type_val(arg_types); - return TCResult(tupleE, tupleT, new_env); - } - case GetField: { - auto res = typecheck_exp(e->u.get_field.aggregate, env, ct_env, 0, - ValueContext); - auto t = res.type; - switch (t->tag) { - case StructTV: - // Search for a field - for (auto vt = t->u.struct_type.fields->begin(); - vt != t->u.struct_type.fields->end(); ++vt) { - if (*e->u.get_field.field == vt->first) { - Expression* new_e = make_get_field(e->lineno, res.exp, - *e->u.get_field.field); - return TCResult(new_e, vt->second, res.env); - } - } - // Search for a method - for (auto vt = t->u.struct_type.methods->begin(); - vt != t->u.struct_type.methods->end(); ++vt) { - if (*e->u.get_field.field == vt->first) { - Expression* new_e = make_get_field(e->lineno, res.exp, - *e->u.get_field.field); - return TCResult(new_e, vt->second, res.env); - } - } - cerr << e->lineno << ": compilation error, struct " - << * t->u.struct_type.name - << " does not have a field named " << * e->u.get_field.field - << endl; - exit(-1); - case TupleTV: - for (auto vt = t->u.tuple_type.fields->begin(); - vt != t->u.tuple_type.fields->end(); ++vt) { - if (*e->u.get_field.field == vt->first) { - auto new_e = make_get_field(e->lineno, res.exp, - *e->u.get_field.field); - return TCResult(new_e, vt->second, res.env); - } - } - cerr << e->lineno << ": compilation error, struct " - << * t->u.struct_type.name - << " does not have a field named " << * e->u.get_field.field - << endl; - exit(-1); - case ChoiceTV: - for (auto vt = t->u.choice_type.alternatives->begin(); - vt != t->u.choice_type.alternatives->end(); ++vt) { - if (*e->u.get_field.field == vt->first) { - Expression* new_e = make_get_field(e->lineno, res.exp, - *e->u.get_field.field); - auto fun_ty = make_fun_type_val(vt->second, t); - return TCResult(new_e, fun_ty, res.env); - } - } - cerr << e->lineno << ": compilation error, struct " - << * t->u.struct_type.name - << " does not have a field named " << * e->u.get_field.field - << endl; - exit(-1); - - default: - cerr << e->lineno - << ": compilation error in field access, expected a struct" - << endl; - print_exp(e); - cerr << endl; - exit(-1); - } - } - case Variable: { - auto t = lookup(e->lineno, env, *(e->u.variable.name), print_error_string); - return TCResult(e, t, env); - } - case Integer: - return TCResult(e, make_int_type_val(), env); - break; - case Boolean: - return TCResult(e, make_bool_type_val(), env); - break; - case PrimitiveOp: { - auto es = new vector(); - vector ts; - auto new_env = env; - for (auto iter = e->u.primitive_op.arguments->begin(); - iter != e->u.primitive_op.arguments->end(); ++iter) { - auto res = typecheck_exp(*iter, env, ct_env, 0, ValueContext); - new_env = res.env; - es->push_back(res.exp); - ts.push_back(res.type); - } - auto new_e = make_op(e->lineno, e->u.primitive_op.operator_, es); - switch (e->u.primitive_op.operator_) { - case Neg: - expect_type(e->lineno, "negation", make_int_type_val(), ts[0]); - return TCResult(new_e, make_int_type_val(), new_env); - case Add: - case Sub: - expect_type(e->lineno, "subtraction(1)", make_int_type_val(), ts[0]); - expect_type(e->lineno, "substration(2)", make_int_type_val(), ts[1]); - return TCResult(new_e, make_int_type_val(), new_env); - case And: - expect_type(e->lineno, "&&(1)", make_bool_type_val(), ts[0]); - expect_type(e->lineno, "&&(2)", make_bool_type_val(), ts[1]); - return TCResult(new_e, make_bool_type_val(), new_env); - case Or: - expect_type(e->lineno, "||(1)", make_bool_type_val(), ts[0]); - expect_type(e->lineno, "||(2)", make_bool_type_val(), ts[1]); - return TCResult(new_e, make_bool_type_val(), new_env); - case Not: - expect_type(e->lineno, "!", make_bool_type_val(), ts[0]); - return TCResult(new_e, make_bool_type_val(), new_env); - case Eq: - expect_type(e->lineno, "==(1)", make_int_type_val(), ts[0]); - expect_type(e->lineno, "==(2)", make_int_type_val(), ts[1]); - return TCResult(new_e, make_bool_type_val(), new_env); - } - break; - } - case Call: { - auto fun_res = typecheck_exp(e->u.call.function, env, ct_env, 0, - ValueContext); - switch (fun_res.type->tag) { - case FunctionTV: { - auto funT = fun_res.type; - auto arg_res = typecheck_exp(e->u.call.argument, - fun_res.env, ct_env, - funT->u.fun_type.param, - ValueContext); - expect_type(e->lineno, "call", funT->u.fun_type.param, arg_res.type); - auto new_e = make_call(e->lineno, fun_res.exp, arg_res.exp); - return TCResult(new_e, funT->u.fun_type.ret, arg_res.env); - } - default: { - cerr << e->lineno << ": compilation error in call, expected a function" - << endl; - print_exp(e); - cerr << endl; - exit(-1); - } - } - break; - } - case FunctionT: { - switch (context) { - case ValueContext: - case TypeContext: { - auto pt = to_type(e->lineno, - interp_exp(ct_env, e->u.function_type.parameter)); - auto rt = to_type(e->lineno, - interp_exp(ct_env, e->u.function_type.return_type)); - auto new_e = make_fun_type(e->lineno, reify_type(pt, e->lineno), - reify_type(rt, e->lineno)); - return TCResult(new_e, make_type_type_val(), env); - } - case PatternContext: { - auto param_res = typecheck_exp(e->u.function_type.parameter, env, ct_env, - 0, context); - auto ret_res = typecheck_exp(e->u.function_type.return_type, - param_res.env, ct_env, 0, context); - auto new_e = make_fun_type(e->lineno, reify_type(param_res.type, - e->lineno), - reify_type(ret_res.type, e->lineno)); - return TCResult(new_e, make_type_type_val(), ret_res.env); - } - } - } - case IntT: case BoolT: case TypeT: case AutoT: - return TCResult(e, make_type_type_val(), env); - } -} - -pair -typecheck_case( Value* expected, Expression* pat, - Statement* body, TypeEnv* env, Env* ct_env, - Value* ret_type) { - auto pat_res = typecheck_exp(pat, env, ct_env, expected, PatternContext); - auto res = typecheck_stmt(body, pat_res.env, ct_env, ret_type); - return make_pair(pat, res.stmt); -} - -TCStatement -typecheck_stmt(Statement* s, TypeEnv* env, Env* ct_env, - Value* ret_type) { - if (! s) { - return TCStatement(s, env); - } - switch (s->tag) { - case Match: { - auto res = typecheck_exp(s->u.match_stmt.exp, env, ct_env, 0, ValueContext); - auto res_type = res.type; - auto new_clauses = new list< pair >(); - for (auto i = s->u.match_stmt.clauses->begin(); - i != s->u.match_stmt.clauses->end(); ++i) { - new_clauses->push_back(typecheck_case(res_type, - i->first, i->second, - env, ct_env, ret_type)); - } - Statement* new_s = make_match(s->lineno, res.exp, new_clauses); - return TCStatement(new_s, env); - } - case While: { - auto cnd_res = typecheck_exp(s->u.while_stmt.cond, env, ct_env, 0, - ValueContext); - expect_type(s->lineno, "condition of `while`", make_bool_type_val(), - cnd_res.type); - auto body_res = typecheck_stmt(s->u.while_stmt.body, - env, ct_env, ret_type); - auto new_s = make_while(s->lineno, cnd_res.exp, body_res.stmt); - return TCStatement(new_s, env); - } - case Break: - return TCStatement(s, env); - case Continue: - return TCStatement(s, env); - case Block: { - auto stmt_res = typecheck_stmt(s->u.block.stmt, - env, ct_env, ret_type); - return TCStatement(make_block(s->lineno, stmt_res.stmt), env); - } - case VariableDefinition: { - auto res = typecheck_exp(s->u.variable_definition.init, - env, ct_env, 0, ValueContext); - Value* rhs_ty = res.type; - auto lhs_res = typecheck_exp(s->u.variable_definition.pat, - env, ct_env, rhs_ty, PatternContext); - Statement* new_s = make_var_def(s->lineno, s->u.variable_definition.pat, - res.exp); - return TCStatement(new_s, lhs_res.env); - } - case Sequence: { - auto stmt_res = typecheck_stmt(s->u.sequence.stmt, env, ct_env, - ret_type); - auto env2 = stmt_res.env; - auto next_res = typecheck_stmt(s->u.sequence.next, env2, ct_env, - ret_type); - auto env3 = next_res.env; - return TCStatement(make_seq(s->lineno, stmt_res.stmt, next_res.stmt), env3); - } - case Assign: { - auto rhs_res = typecheck_exp(s->u.assign.rhs, env, ct_env, 0, ValueContext); - auto rhsT = rhs_res.type; - auto lhs_res = typecheck_exp(s->u.assign.lhs, env, ct_env, rhsT, - ValueContext); - auto lhsT = lhs_res.type; - expect_type(s->lineno, "assign", lhsT, rhsT); - auto new_s = make_assign(s->lineno, lhs_res.exp, rhs_res.exp); - return TCStatement(new_s, lhs_res.env); - } - case ExpressionStatement: { - auto res = typecheck_exp(s->u.exp, env, ct_env, 0, ValueContext); - auto new_s = make_exp_stmt(s->lineno, res.exp); - return TCStatement(new_s, env); - } - case If: { - auto cnd_res = typecheck_exp(s->u.if_stmt.cond, env, ct_env, 0, - ValueContext); - expect_type(s->lineno, "condition of `if`", make_bool_type_val(), - cnd_res.type); - auto thn_res = typecheck_stmt(s->u.if_stmt.thn, env, ct_env, - ret_type); - auto els_res = typecheck_stmt(s->u.if_stmt.els, env, ct_env, - ret_type); - auto new_s = make_if(s->lineno, cnd_res.exp, thn_res.stmt, els_res.stmt); - return TCStatement(new_s, env); - } - case Return: { - auto res = typecheck_exp(s->u.return_stmt, env, ct_env, 0, ValueContext); - if (ret_type->tag == AutoTV) { - // The following infers the return type from the first 'return' statement. - // This will get more difficult with subtyping, when we should infer - // the least-upper bound of all the 'return' statements. - *ret_type = *res.type; - } else { - expect_type(s->lineno, "return", ret_type, res.type); - } - return TCStatement(make_return(s->lineno, res.exp), env); - } - } -} - - -Statement* check_or_ensure_return(Statement* stmt, bool void_return, - int lineno) { - if (! stmt) { - if (void_return) { - auto args = new vector >(); - return make_return(lineno, make_tuple(lineno, args)); - } else { - cerr << "control-flow reaches end of non-void function without a return" - << endl; - exit(-1); - } - } - switch (stmt->tag) { - case Match: { - auto new_clauses = new list< pair >(); - for (auto i = stmt->u.match_stmt.clauses->begin(); - i != stmt->u.match_stmt.clauses->end(); ++i) { - auto s = check_or_ensure_return(i->second, void_return, stmt->lineno); - new_clauses->push_back(make_pair(i->first, s)); - } - return make_match(stmt->lineno, stmt->u.match_stmt.exp, new_clauses); - } - case Block: - return make_block(stmt->lineno, - check_or_ensure_return(stmt->u.block.stmt, void_return, - stmt->lineno)); - case If: - return make_if(stmt->lineno, stmt->u.if_stmt.cond, - check_or_ensure_return(stmt->u.if_stmt.thn, void_return, - stmt->lineno), - check_or_ensure_return(stmt->u.if_stmt.els, void_return, - stmt->lineno)); - case Return: - return stmt; - case Sequence: - if (stmt->u.sequence.next) { - return make_seq(stmt->lineno, stmt->u.sequence.stmt, - check_or_ensure_return(stmt->u.sequence.next, - void_return, stmt->lineno)); - } else { - return check_or_ensure_return(stmt->u.sequence.stmt, void_return, - stmt->lineno); - } - case Assign: - case ExpressionStatement: - case While: - case Break: - case Continue: - case VariableDefinition: - if (void_return) { - auto args = new vector >(); - return make_seq(stmt->lineno, stmt, - make_return(stmt->lineno, - make_tuple(stmt->lineno, args))); - } else { - cerr << stmt->lineno - << ": control-flow reaches end of non-void function without a return" - << endl; - exit(-1); - } - } -} - -struct FunctionDefinition* -typecheck_fun_def(struct FunctionDefinition* f, - TypeEnv* env, Env* ct_env) { - auto param_res = typecheck_exp(f->param_pattern, env, ct_env, 0, - PatternContext); - auto return_type = to_type(f->lineno, interp_exp(ct_env, f->return_type)); - if (f->name == "main") { - expect_type(f->lineno, "return type of `main`", - make_int_type_val(), return_type); - // todo: check that main doesn't have any parameters - } - auto res = typecheck_stmt(f->body, param_res.env, ct_env, return_type); - bool void_return = type_equal(return_type, make_void_type_val()); - auto body = check_or_ensure_return(res.stmt, void_return, f->lineno); - return make_fun_def(f->lineno, f->name, reify_type(return_type, f->lineno), - f->param_pattern, body); -} - -Value* type_of_fun_def( TypeEnv* env, Env* ct_env, - struct FunctionDefinition* fun_def) { - auto param_res = typecheck_exp(fun_def->param_pattern, env, ct_env, 0, - PatternContext); - auto param_type = to_type(fun_def->lineno, param_res.type); - auto ret = interp_exp(ct_env, fun_def->return_type); - if (ret->tag == AutoTV) { - auto f = typecheck_fun_def(fun_def, env, ct_env); - ret = interp_exp(ct_env, f->return_type); - } - return make_fun_type_val(param_type, ret); -} - -Value* type_of_struct_def(struct StructDefinition* sd, TypeEnv* env, Env* ct_top) { - auto fields = new VarValues(); - auto methods = new VarValues(); - for (auto m = sd->members->begin(); - m != sd->members->end(); ++m) { - if ((*m)->tag == FieldMember) { - auto t = to_type(sd->lineno, - interp_exp(ct_top, (*m)->u.field.type)); - fields->push_back(make_pair(* (*m)->u.field.name, t)); - } - } - return make_struct_type_val(* sd->name, fields, methods); -} - -string name_of_decl(Declaration* d) { - switch (d->tag) { - case FunctionDeclaration: - return d->u.fun_def->name; - case StructDeclaration: - return * d->u.struct_def->name; - case ChoiceDeclaration: - return * d->u.choice_def.name; - } -} - -Declaration* typecheck_decl(Declaration* d, TypeEnv* env, - Env* ct_env) { - switch (d->tag) { - case StructDeclaration: { - auto struct_type = type_of_struct_def(d->u.struct_def, env, ct_env); - auto members = new list(); - for (auto m = d->u.struct_def->members->begin(); - m != d->u.struct_def->members->end(); ++m) { - switch ((*m)->tag) { - case FieldMember: { - // TODO interpret the type expression and store the result. - members->push_back(*m); - break; - } - } - } - return make_struct_decl(d->u.struct_def->lineno, - *d->u.struct_def->name, members); - } - case FunctionDeclaration: - return make_fun_decl(typecheck_fun_def(d->u.fun_def, env, ct_env)); - case ChoiceDeclaration: - return d; // TODO - } -} - - -pair top_level(list* fs) { - TypeEnv* top = 0; - Env* ct_top = 0; - bool found_main = false; - for (auto i = fs->begin(); i != fs->end(); ++i) { - auto d = *i; - if (name_of_decl(d) == "main") { - found_main = true; - } - switch (d->tag) { - case FunctionDeclaration: { - auto t = type_of_fun_def(top, ct_top, d->u.fun_def); - top = new TypeEnv(name_of_decl(d), t, top); - break; - } - case StructDeclaration: { - auto st = type_of_struct_def(d->u.struct_def, top, ct_top); - address a = allocate_value(st); - ct_top = new Env(name_of_decl(d), a, ct_top); // is this obsolete? - auto params = make_tuple_type_val(st->u.struct_type.fields); - auto fun_ty = make_fun_type_val(params, st); - top = new TypeEnv(name_of_decl(d), fun_ty, top); - break; - } - case ChoiceDeclaration: { - auto alts = new VarValues(); - for (auto i = d->u.choice_def.alternatives->begin(); - i != d->u.choice_def.alternatives->end(); ++i) { - auto t = to_type(d->u.choice_def.lineno, - interp_exp(ct_top, i->second)); - alts->push_back(make_pair(i->first, t)); - } - auto ct = make_choice_type_val(d->u.choice_def.name, alts); - address a = allocate_value(ct); - ct_top = new Env(name_of_decl(d), a, ct_top); // is this obsolete? - top = new TypeEnv(name_of_decl(d), ct, top); - break; - } - } // switch (d->tag) - } // for - if (found_main == false) { - cerr << "error, program must contain a function named `main`" << endl; - exit(-1); - } - return make_pair(top, ct_top); -} diff --git a/executable-semantics/typecheck.h b/executable-semantics/typecheck.h deleted file mode 100644 index fdeee72976216..0000000000000 --- a/executable-semantics/typecheck.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef TYPECHECK_H -#define TYPECHECK_H - -#include -using std::set; -using std::pair; -#include "ast.h" -#include "assoc_list.h" -#include "interp.h" - -typedef AList TypeEnv; - -void print_type_env(TypeEnv* env); - -enum TCContext { ValueContext, PatternContext, TypeContext }; - -struct TCResult { - TCResult(Expression* e, Value* t, TypeEnv* env) - : exp(e), type(t), env(env) { } - Expression* exp; - Value* type; - TypeEnv* env; -}; - -struct TCStatement { - TCStatement(Statement* s, TypeEnv* e) : stmt(s), env(e) { } - Statement* stmt; - TypeEnv* env; -}; - -Value* to_type(int lineno, Value* val); - -bool type_equal(Value* t1, Value* t2); -bool fields_equal(VarValues* ts1, VarValues* ts2); - -TCResult typecheck_exp(Expression* e, TypeEnv* env, Env* ct_env, - Value* expected, TCContext context); - -TCStatement typecheck_stmt(Statement*, TypeEnv*, Env*, Value*); - -struct FunctionDefinition* -typecheck_fun_def(struct FunctionDefinition*, TypeEnv*); - -Declaration* typecheck_decl(Declaration* d, TypeEnv* env, Env* ct_env); - -pair top_level(list* fs); - -void print_error_string(string s); - -#endif