From: Michael D. Lowis Date: Mon, 28 Dec 2015 01:29:57 +0000 (-0500) Subject: Half-implemented ANF transformation X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=d10d8c0694edc2d125b8aeedfc8e371a585b4f06;p=proto%2Fsclpl.git Half-implemented ANF transformation --- diff --git a/Makefile b/Makefile index 654a84d..c6fe2cf 100644 --- a/Makefile +++ b/Makefile @@ -16,14 +16,13 @@ LDFLAGS += ${LIBS} #------------------------------------------------------------------------------ # Build Targets and Rules #------------------------------------------------------------------------------ -SRCS = source/main.c \ - source/lexer.c \ - source/parser.c \ - source/pprint.c \ - source/gc.c \ - source/vec.c \ - source/ast.c -OBJS = ${SRCS:.c=.o} +OBJS = source/main.o \ + source/lexer.o \ + source/parser.o \ + source/pprint.o \ + source/gc.o \ + source/vec.o \ + source/ast.o all: options sclpl test diff --git a/source/ast.c b/source/ast.c index 460cfa2..561bef3 100644 --- a/source/ast.c +++ b/source/ast.c @@ -34,10 +34,6 @@ static void ast_free(void* ptr) vec_deinit(&(ast->value.fnapp.args)); break; - case AST_BLOCK: - vec_deinit(&(ast->value.exprs)); - break; - case AST_LET: break; @@ -233,28 +229,6 @@ void ifexpr_set_else(AST* ifexpr, AST* belse) ifexpr->value.ifexpr.belse = (AST*)gc_addref(belse); } -AST* Block(void) -{ - AST* node = ast(AST_BLOCK); - vec_init(&(node->value.exprs)); - return node; -} - -void block_append(AST* block, AST* expr) -{ - vec_push_back(&(block->value.exprs), gc_addref(expr)); -} - -size_t block_size(AST* block) -{ - return vec_size(&(block->value.exprs)); -} - -AST* block_get(AST* block, size_t index) -{ - return (AST*)vec_at(&(block->value.exprs), index); -} - AST* Func(void) { AST* node = ast(AST_FUNC); diff --git a/source/main.c b/source/main.c index e8a21a7..19f45b9 100644 --- a/source/main.c +++ b/source/main.c @@ -4,6 +4,8 @@ char* ARGV0; bool Verbose = false; char* Artifact = "bin"; +AST* normalize(AST* tree); + bool isatomic(AST* tree) { switch (tree->type) { @@ -21,9 +23,11 @@ bool isatomic(AST* tree) } } -//AST* normalize_fnapp_args(AST* tree) -//{ -//} +AST* normalize_def(AST* tree) +{ + Tok name = { .value.text = def_name(tree) }; + return Def(&name, normalize(def_value(tree))); +} AST* normalize_fnapp(AST* tree) { @@ -57,12 +61,65 @@ AST* normalize_fnapp(AST* tree) return normalized; } +AST* normalize_if(AST* tree) +{ + AST* cond = normalize(ifexpr_cond(tree)); + AST* thenbr = normalize(ifexpr_then(tree)); + AST* elsebr = normalize(ifexpr_else(tree)); + if (!isatomic(cond)) { + AST* temp = TempVar(); + AST* body = IfExpr(); //(temp, thenbr, elsebr); + ifexpr_set_cond(body, temp); + ifexpr_set_then(body, thenbr); + ifexpr_set_else(body, elsebr); + tree = Let(temp, cond, body); + } else { + tree = IfExpr(); //(cond, thenbr, elsebr); + ifexpr_set_cond(tree, cond); + ifexpr_set_then(tree, thenbr); + ifexpr_set_else(tree, elsebr); + } + return tree; +} + +AST* normalize_func(AST* tree) +{ + func_set_body(tree, normalize(func_body(tree))); + return tree; +} + +AST* normalize_let(AST* tree) +{ + AST* var = let_var(tree); + AST* val = normalize(let_val(tree)); + AST* body = normalize(let_body(tree)); + /* Find the inner most let block */ + if (!isatomic(val)) { + AST* let = val; + while (let->type == AST_LET && let_body(let)->type == AST_LET) + let = let_body(let); + let_set_body(let, Let(var, let_body(let), body)); + tree = let; + } else { + tree = Let(var, val, body); + } + return tree; +} + AST* normalize(AST* tree) { - if (tree->type == AST_FNAPP) - return normalize_fnapp(tree); - else + if (NULL == tree) return tree; + switch (tree->type) + { + case AST_DEF: tree = normalize_def(tree); break; + case AST_FNAPP: tree = normalize_fnapp(tree); break; + case AST_IF: tree = normalize_if(tree); break; + case AST_FUNC: tree = normalize_func(tree); break; + case AST_LET: tree = normalize_let(tree); break; + default: break; + } + return tree; } /* Driver Modes diff --git a/source/parser.c b/source/parser.c index fab0d62..8ba7a8b 100644 --- a/source/parser.c +++ b/source/parser.c @@ -141,10 +141,27 @@ static AST* literal(Parser* p) static AST* expr_block(Parser* p) { - AST* block = Block(); + AST* block = NULL; + vec_t exprs; + vec_init(&exprs); + /* Build all expressions into let forms with no bodies */ do { - block_append(block, expression(p)); + if (accept_str(p, T_ID, "def")) { + AST* def = definition(p); + Tok name = { .value.text = def_name(def) }; + vec_push_back(&exprs, Let(Ident(&name), def_value(def), NULL)); + } else { + vec_push_back(&exprs, Let(TempVar(), expression(p), NULL)); + } } while(!match(p, T_END) && !match_str(p, T_ID, "else")); + /* Now nest all of the let forms making sure that the last one returns + * it's definition as its body */ + for (int i = vec_size(&exprs); i > 0; i--) { + AST* let = (AST*)vec_at(&exprs,i-1); + let_set_body(let, (block == NULL) ? let_var(let) : block); + block = let; + } + vec_deinit(&exprs); return block; } diff --git a/source/pprint.c b/source/pprint.c index 0cc839d..41bf2f3 100644 --- a/source/pprint.c +++ b/source/pprint.c @@ -118,8 +118,9 @@ static void pprint_literal(FILE* file, AST* tree, int depth) void pprint_tree(FILE* file, AST* tree, int depth) { - if (tree == NULL) + if (tree == NULL) { return; + } print_indent(file, depth); switch (tree->type) { case AST_REQ: @@ -142,15 +143,6 @@ void pprint_tree(FILE* file, AST* tree, int depth) printf(")"); break; - case AST_BLOCK: - printf("(block "); - for (size_t i = 0; i < block_size(tree); i++) { - printf(" "); - pprint_tree(file, block_get(tree, i), depth); - } - printf(")"); - break; - case AST_FUNC: printf("(fn ("); for (size_t i = 0; i < vec_size(func_args(tree)); i++) { @@ -158,10 +150,7 @@ void pprint_tree(FILE* file, AST* tree, int depth) pprint_literal(file, vec_at(func_args(tree), i), depth); } printf(")"); - for (size_t i = 0; i < block_size(func_body(tree)); i++) { - printf(" "); - pprint_tree(file, block_get(func_body(tree), i), depth); - } + pprint_tree(file, func_body(tree), depth); printf(")"); break; diff --git a/source/sclpl.h b/source/sclpl.h index 92c5a44..9ddc8d8 100644 --- a/source/sclpl.h +++ b/source/sclpl.h @@ -74,7 +74,7 @@ typedef struct { *****************************************************************************/ typedef enum ASTType { AST_STRING, AST_SYMBOL, AST_CHAR, AST_INT, AST_FLOAT, AST_BOOL, AST_IDENT, - AST_REQ, AST_DEF, AST_IF, AST_FUNC, AST_FNAPP, AST_BLOCK, AST_LET, AST_TEMP + AST_REQ, AST_DEF, AST_IF, AST_FUNC, AST_FNAPP, AST_LET, AST_TEMP } ASTType; typedef struct AST { @@ -107,8 +107,6 @@ typedef struct AST { struct AST* value; struct AST* body; } let; - /* Code Block */ - vec_t exprs; /* String, Symbol, Identifier */ char* text; /* Character */ @@ -172,12 +170,6 @@ void ifexpr_set_then(AST* ifexpr, AST* bthen); AST* ifexpr_else(AST* ifexpr); void ifexpr_set_else(AST* ifexpr, AST* belse); -/* Code Block */ -AST* Block(void); -void block_append(AST* block, AST* expr); -size_t block_size(AST* block); -AST* block_get(AST* block, size_t index); - /* Function */ AST* Func(void); vec_t* func_args(AST* func); diff --git a/spec/anf_spec.rb b/spec/anf_spec.rb index 30342ad..a22ee6d 100644 --- a/spec/anf_spec.rb +++ b/spec/anf_spec.rb @@ -27,6 +27,16 @@ describe "sclpl a-normal form" do end end + context "definitions" do + it "should leave atomic defintions alone" do + expect(anf('def foo 123;')).to eq([["def", "foo", "T_INT:123"]]) + end + + it "should leave normalize complex defintions" do + expect(anf('def foo bar();')).to eq([["def", "foo", ["T_ID:bar"]]]) + end + end + context "function applications" do it "should leave an application with no args alone" do expect(anf('foo()')).to eq([['T_ID:foo']]) @@ -80,4 +90,47 @@ describe "sclpl a-normal form" do ['T_ID:foo', '$:0', '$:1', 'T_ID:a']]]]) end end + + context "if expressions" do + it "should leave atomic expressions alone" do + expect(anf('if 1 2 3;')).to eq([ + ["if", "T_INT:1", + ["let", ["$:0", "T_INT:2"], + ["let", ["$:1", "T_INT:3"], + "$:1"]]]]) + end + + it "should normalize the conditional expression" do + expect(anf('if foo() 2 else 3;')).to eq([ + ["let", ["$:2", ["T_ID:foo"]], + ["if", "$:2", + ["let", ["$:0", "T_INT:2"], "$:0"], + ["let", ["$:1", "T_INT:3"], "$:1"]]]]) + end + + it "should normalize the first branch expression" do + expect(anf('if 1 foo() else 3;')).to eq([ + ["if", "T_INT:1", + ["let", ["$:0", ["T_ID:foo"]], "$:0"], + ["let", ["$:1", "T_INT:3"], "$:1"]]]) + end + + it "should normalize the first branch expression" do + expect(anf('if 1 2 else foo();')).to eq([ + ["if", "T_INT:1", + ["let", ["$:0", "T_INT:2"], "$:0"], + ["let", ["$:1", ["T_ID:foo"]], "$:1"]]]) + end + end + + context "function literals" do + it "should normalize a literal with a complex expression" do + expect(anf('fn() foo(bar());')).to eq([ + ["fn", [], + ["let", ["$:1", ["T_ID:bar"]], + ["let", ["$:0", ["T_ID:foo", "$:1"]], + "$:0"]]] + ]) + end + end end diff --git a/spec/parser_spec.rb b/spec/parser_spec.rb index 49ec174..a6eeed7 100644 --- a/spec/parser_spec.rb +++ b/spec/parser_spec.rb @@ -56,78 +56,38 @@ describe "sclpl grammar" do context "if statements" do it "should parse an if statement with no else clause" do expect(ast('if 123 321 end')).to eq([ - ["if", "T_INT:123", ["block", "T_INT:321"]]]) + ["if", "T_INT:123", ["let", ["$:0", "T_INT:321"], "$:0"]]]) end it "should parse an if statement with an optional then keyword" do expect(ast('if 123 then 321 end')).to eq([ - ["if", "T_INT:123", ["block", "T_INT:321"]]]) + ["if", "T_INT:123", ["let", ["$:0", "T_INT:321"], "$:0"]]]) end it "should parse an if statement with an else clause " do expect(ast('if 123 321 else 456 end')).to eq([ - ["if", "T_INT:123", ["block", "T_INT:321"], ["block", "T_INT:456"]]]) + ["if", "T_INT:123", + ["let", ["$:0", "T_INT:321"], "$:0"], + ["let", ["$:1", "T_INT:456"], "$:1"]]]) end - end -# context "type definitions" do -# it "should parse a simple type definition" do -# expect(ast('type foo is int;')).to eq([ ['T_ID:type', 'T_ID:foo', 'T_ID:is', 'T_ID:int'] ]) -# end -# -# it "should parse a function type definition with no args" do -# expect(ast('type foo is int();')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:int', []]] ]) -# end -# -# it "should parse a function type definition with one arg" do -# expect(ast('type foo is int(int);')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:int', ['T_ID:int']]] ]) -# end -# -# it "should parse a function type definition with two args" do -# expect(ast('type foo is int(int,int);')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:int', ['T_ID:int', 'T_ID:int']]] ]) -# end -# -# it "should parse a function type definition with three args" do -# expect(ast('type foo is int(int,int,int);')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:int', ['T_ID:int', 'T_ID:int', 'T_ID:int']]] ]) -# end -# -# it "should parse a tuple type definition with one field" do -# expect(ast('type foo is {int};')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:tuple', 'T_ID:int']] ]) -# end -# -# it "should parse a tuple type definition with two fields" do -# expect(ast('type foo is {int,int};')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:tuple', 'T_ID:int', 'T_ID:int']] ]) -# end -# -# it "should parse a tuple type definition with three fields" do -# expect(ast('type foo is {int,int,int};')).to eq([ -# ['T_ID:type', 'T_ID:foo', 'T_ID:is', ['T_ID:tuple', 'T_ID:int', 'T_ID:int', 'T_ID:int']] ]) -# end -# -# it "should parse a record type definition with one field" do -# pending "Type annotations have not been implemented yet" -# expect(ast('type foo is { int a };')).to eq([ -# ['T_ID:type', 'T_ID:foo', ['T_ID:record', ['T_ID:int', 'T_ID:a']]] ]) -# end -# -# it "should parse a record type definition with two fields" do -# pending "Type annotations have not been implemented yet" -# expect(ast('type foo is { int a, int b };')).to eq([ -# ['T_ID:type', 'T_ID:foo', ['T_ID:record', ['T_ID:int', 'T_ID:a'], ['T_ID:int', 'T_ID:b']]] ]) -# end -# -# it "should parse a record type definition with three fields" do -# pending "Type annotations have not been implemented yet" -# expect(ast('type foo is { int a, int b };')).to eq([ -# ['T_ID:type', 'T_ID:foo', ['T_ID:record', ['T_ID:int', 'T_ID:a'], ['T_ID:int', 'T_ID:b'], ['T_ID:int', 'T_ID:c']]] ]) -# end -# end + it "should parse an if statement with a then clause with multiple expressions" do + expect(ast('if 1 2 3 else 4 end')).to eq([ + ["if", "T_INT:1", + ["let", ["$:0", "T_INT:2"], + ["let", ["$:1", "T_INT:3"], "$:1"]], + ["let", ["$:2", "T_INT:4"], "$:2"]]]) + end + + it "should parse an if statement with an else clause with multiple expressions" do + expect(ast('if 1 2 else 3 4 end')).to eq([ + ["if", "T_INT:1", + ["let", ["$:0", "T_INT:2"], "$:0"], + ["let", ["$:1", "T_INT:3"], + ["let", ["$:2", "T_INT:4"], "$:2"]], + ]]) + end + end context "definitions" do it "should parse a value definition" do @@ -136,50 +96,43 @@ describe "sclpl grammar" do it "should parse a function definition" do expect(ast('def foo() 123;')).to eq([ - ['def', 'foo', ['fn', [], 'T_INT:123']] ]) + ['def', 'foo', ['fn', [], + ["let", ["$:0", "T_INT:123"], "$:0"]]] ]) end it "should parse a function definition with multiple expressions in the body" do expect(ast('def foo() 123 321;')).to eq([ - ['def', 'foo', ['fn', [], 'T_INT:123', 'T_INT:321']] ]) + ['def', 'foo', ['fn', [], + ["let", ["$:0", "T_INT:123"], + ["let", ["$:1", "T_INT:321"], "$:1"]]]]]) + end + + it "should parse a function definition with one definition and expression" do + expect(ast('def foo() def bar 123; add(bar,321);')).to eq([ + ['def', 'foo', ['fn', [], + ["let", ["T_ID:bar", "T_INT:123"], + ["let", ["$:0", ["T_ID:add", "T_ID:bar", "T_INT:321"]], "$:0"]]]]]) end it "should parse a function definition with one argument" do expect(ast('def foo(a) 123;')).to eq([ - ['def', 'foo', ['fn', ['T_ID:a'], 'T_INT:123']] ]) + ['def', 'foo', ['fn', ['T_ID:a'], + ["let", ["$:0", "T_INT:123"], "$:0"]]]]) end it "should parse a function definition with two arguments" do expect(ast('def foo(a,b) 123;')).to eq([ - ['def', 'foo', ['fn', ['T_ID:a', 'T_ID:b'], 'T_INT:123']] ]) + ['def', 'foo', ['fn', ['T_ID:a', 'T_ID:b'], + ["let", ["$:0", "T_INT:123"], "$:0"]]]]) end it "should parse a function definition with three arguments" do expect(ast('def foo(a,b,c) 123;')).to eq([ - ['def', 'foo', ['fn', ['T_ID:a', 'T_ID:b', 'T_ID:c'], 'T_INT:123']] ]) + ['def', 'foo', ['fn', ['T_ID:a', 'T_ID:b', 'T_ID:c'], + ["let", ["$:0", "T_INT:123"], "$:0"]]]]) end end -# context "annotations" do -# it "should parse a type annotation for a simple type" do -# expect(ast('ann foo int;')).to eq([ ['T_ID:ann', 'T_ID:foo', 'T_ID:int'] ]) -# end -# -# it "should parse a type annotation for a function type" do -# expect(ast('ann foo int();')).to eq([ ['T_ID:ann', 'T_ID:foo', ['T_ID:int', []]] ]) -# end -# -# it "should parse a type annotation for a tuple type" do -# expect(ast('ann foo {int, int};')).to eq([ ['T_ID:ann', 'T_ID:foo', ['T_ID:tuple', 'T_ID:int', 'T_ID:int']] ]) -# end -# -# it "should parse a type annotation with a generic type" do -# pending "Type annotations have not been implemented yet" -# expect(ast('ann foo Pair[int,int];')).to eq([ -# ['T_ID:ann', 'T_ID:foo', ['T_ID:Pair', 'T_ID:int', 'T_ID:int']] ]) -# end -# end -# context "expressions" do context "parenthese grouping" do it "should parse a parenthesized expression" do @@ -193,15 +146,21 @@ describe "sclpl grammar" do context "function literals" do it "should parse a function with no params" do - expect(ast('fn() 123;')).to eq([["fn", [], "T_INT:123"]]) + expect(ast('fn() 123;')).to eq([ + ["fn", [], + ["let", ["$:0", "T_INT:123"], "$:0"]]]) end it "should parse a function with one param" do - expect(ast('fn(a) 123;')).to eq([["fn", ["T_ID:a"], "T_INT:123"]]) + expect(ast('fn(a) 123;')).to eq([ + ["fn", ["T_ID:a"], + ["let", ["$:0", "T_INT:123"], "$:0"]]]) end it "should parse a function with two params" do - expect(ast('fn(a,b) 123;')).to eq([["fn", ["T_ID:a", "T_ID:b"], "T_INT:123"]]) + expect(ast('fn(a,b) 123;')).to eq([ + ["fn", ["T_ID:a", "T_ID:b"], + ["let", ["$:0", "T_INT:123"], "$:0"]]]) end end