From f27220a4cd282601523c49ce6cf194e1d1077242 Mon Sep 17 00:00:00 2001 From: "Michael D. Lowis" Date: Mon, 27 Oct 2014 22:08:02 -0400 Subject: [PATCH] Added specs to test the CLI --- source/sclpl/main.c | 99 ++++++++++++++++++++++++++++++++++--------- source/sclpl/pprint.c | 18 ++++---- spec/cli_spec.rb | 73 +++++++++++++++++++++++++++++++ spec/spec_helper.rb | 22 +++++----- spec/src/sample.scl | 0 5 files changed, 172 insertions(+), 40 deletions(-) create mode 100644 spec/cli_spec.rb create mode 100644 spec/src/sample.scl diff --git a/source/sclpl/main.c b/source/sclpl/main.c index 17b3606..885e3dc 100644 --- a/source/sclpl/main.c +++ b/source/sclpl/main.c @@ -163,6 +163,8 @@ str_t* str_join(char* joinstr, vec_t* strs) { /* Utility Functions *****************************************************************************/ typedef enum { + TOKFILE, + ASTFILE, CSOURCE, OBJECT, PROGRAM, @@ -173,6 +175,8 @@ typedef enum { str_t* get_extension(file_type_t ftype) { str_t* ext = NULL; switch (ftype) { + case TOKFILE: ext = str_new(".tok"); break; + case ASTFILE: ext = str_new(".ast"); break; case CSOURCE: ext = str_new(".c"); break; case OBJECT: ext = str_new(".o"); break; case PROGRAM: ext = str_new(""); break; @@ -196,10 +200,10 @@ str_t* get_filename(file_type_t ftype, str_t* infile) { } vec_t* parse_file(str_t* in) { + bool failed = false; FILE* input = (NULL == in) ? stdin : fopen(str_cstr(in), "r"); parser_t* p_parser = parser_new(NULL, input); vec_t* p_vec = vec_new(0); - bool failed = false; while(!parser_eof(p_parser)) { tree_t* p_tree = grammar_toplevel(p_parser); if (NULL != p_tree) { @@ -216,6 +220,59 @@ vec_t* parse_file(str_t* in) { return ((failed) ? NULL : p_vec); } +vec_t* program_deps(vec_t* program) { + vec_t* deps = vec_new(0); + (void)program; + return deps; +} + +str_t* token_file(str_t* in) { + str_t* ofname = NULL; + FILE* input = (NULL == in) ? stdin : fopen(str_cstr(in), "r"); + FILE* output; + if (NULL == in) { + output = stdout; + } else { + ofname = get_filename(TOKFILE, in); + output = fopen(str_cstr(ofname), "w"); + } + + lexer_t* p_lexer = lexer_new(NULL, input); + lex_tok_t* token; + while(NULL != (token = lexer_read(p_lexer))) { + pprint_token(output, token); + mem_release(token); + } + mem_release(p_lexer); + + return ofname; +} + +str_t* syntax_file(str_t* in) { + str_t* ofname = NULL; + FILE* output; + if (NULL == in) { + output = stdout; + } else { + ofname = get_filename(ASTFILE, in); + output = fopen(str_cstr(ofname), "w"); + } + vec_t* program = parse_file(in); + if (NULL != program) { + for (size_t idx = 0; idx < vec_size(program); idx++) { + pprint_tree(output, (tree_t*)vec_at(program, idx), 0); + } + mem_release(program); + fclose(output); + } else { + fclose(output); + remove(str_cstr(ofname)); + mem_release(ofname); + ofname = NULL; + } + return ofname; +} + str_t* translate_file(str_t* in) { str_t* ofname = NULL; FILE* output; @@ -258,32 +315,34 @@ str_t* link_files(list_t* in) { /* Driver Modes *****************************************************************************/ static int emit_tokens(void) { - lexer_t* p_lexer = lexer_new(NULL, stdin); - lex_tok_t* token; - while(NULL != (token = lexer_read(p_lexer))) { - pprint_token(stdout, token); - mem_release(token); + list_t* files = input_files(); + size_t nfiles = list_size(files); + if (0 == nfiles) { + (void)token_file(NULL); + } else if (1 == nfiles) { + str_t* fname = list_front(files)->contents; + mem_release( token_file(fname) ); + } else { + error_msg("too many files provided for target mode 'tokens'"); } - mem_release(p_lexer); + mem_release(files); return 0; } static int emit_tree(void) { int ret = 0; - parser_t* p_parser = parser_new(NULL, stdin); - while(!parser_eof(p_parser)) { - tree_t* p_tree = grammar_toplevel(p_parser); - if (NULL != p_tree) { - tree_t* p_ast = convert_to_ast(p_tree); - pprint_tree(stdout, p_ast, 0); - mem_release(p_tree); - mem_release(p_ast); - } else { - parser_resume(p_parser); - ret = 1; - } + list_t* files = input_files(); + size_t nfiles = list_size(files); + if (0 == nfiles) { + (void)syntax_file(NULL); + } else if (1 == nfiles) { + str_t* fname = list_front(files)->contents; + mem_release( syntax_file(fname) ); + } else { + error_msg("too many files provided for target mode 'ast'"); } - mem_release(p_parser); + mem_release(files); + return ret; } diff --git a/source/sclpl/pprint.c b/source/sclpl/pprint.c index b6f9758..6d51eab 100644 --- a/source/sclpl/pprint.c +++ b/source/sclpl/pprint.c @@ -59,13 +59,13 @@ void pprint_token_type(FILE* file, lex_tok_t* token) { void pprint_token_value(FILE* file, lex_tok_t* token) { void* value = token->value; switch(token->type) { - case T_STRING: fprintf(file, "\"%s\"", ((char*)value)); break; - case T_CHAR: print_char(file, ((char)(int)value)); break; - case T_INT: fprintf(file, "%ld", *((long int*)value)); break; - case T_FLOAT: fprintf(file, "%f", *((double*)value)); break; - case T_BOOL: fprintf(file, "%s", ((int)value)?"true":"false"); break; - case T_VAR: fprintf(file, "%s", ((char*)value)); break; - default: fprintf(file, "???"); break; + case T_STRING: fprintf(file, "\"%s\"", ((char*)value)); break; + case T_CHAR: print_char(file, ((char)(intptr_t)value)); break; + case T_INT: fprintf(file, "%ld", *((long int*)value)); break; + case T_FLOAT: fprintf(file, "%f", *((double*)value)); break; + case T_BOOL: fprintf(file, "%s", ((intptr_t)value)?"true":"false"); break; + case T_VAR: fprintf(file, "%s", ((char*)value)); break; + default: fprintf(file, "???"); break; } } @@ -86,13 +86,13 @@ void pprint_tree(FILE* file, tree_t* tree, int depth) if (tree->tag == ATOM) { pprint_token(file, tree->ptr.tok); } else { - puts("(tree"); + fputs("(tree", file); vec_t* p_vec = tree->ptr.vec; for(size_t idx = 0; idx < vec_size(p_vec); idx++) { pprint_tree(file, (tree_t*)vec_at(p_vec, idx), depth+1); } print_indent(file, depth); - puts(")"); + fputs(")\n", file); } } diff --git a/spec/cli_spec.rb b/spec/cli_spec.rb new file mode 100644 index 0000000..e4a4480 --- /dev/null +++ b/spec/cli_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +describe "cli" do + context "token mode" do + it "should accept input from stdin" do + expect(cli(['--tokens'])).to eq("") + end + + it "should translate the input file" do + expect(cli(['--tokens', 'spec/src/sample.scl'])).to eq("") + expect(File.exists? 'spec/src/sample.tok').to be(true) + FileUtils.rm('spec/src/sample.tok') + end + end + + context "ast mode" do + it "should accept input from stdin" do + expect(cli(['--ast'])).to eq("") + end + + it "should translate the input file" do + expect(cli(['--ast', 'spec/src/sample.scl'])).to eq("") + expect(File.exists? 'spec/src/sample.ast').to be(true) + FileUtils.rm('spec/src/sample.ast') + end + end + + context "c source mode" do + it "should accept input from stdin" do + expect(cli(['--csource'])).not_to eq("") + end + + it "should translate the input file" do + expect(cli(['--csource', 'spec/src/sample.scl'])).to eq("") + expect(File.exists? 'spec/src/sample.c').to be(true) + FileUtils.rm('spec/src/sample.c') + end + end + + context "object mode" do + it "should error when too few input files provided" do + expect{cli(['--object'])}.to raise_error(/too few files/) + end + + it "should error when too many input files provided" do + expect{cli(['--object', 'spec/src/sample.scl', 'spec/src/sample.scl'])}.to raise_error(/too many files/) + end + + it "should compile the input file in verbose mode (short flag)" do + pending "Can't find the sclpl header" + expect(cli(['-v', '--object', 'spec/src/sample.scl'])).to eq( + "clang -c -o spec/src/sample.o spec/src/sample.c") + expect(File.exists? 'spec/src/sample.o').to be(true) + FileUtils.rm('spec/src/sample.o') + end + + it "should compile the input file in verbose mode (long flag)" do + pending "Can't find the sclpl header" + expect(cli(['--verbose', '--object', 'spec/src/sample.scl'])).to eq( + "clang -c -o spec/src/sample.o spec/src/sample.c") + expect(File.exists? 'spec/src/sample.o').to be(true) + FileUtils.rm('spec/src/sample.o') + end + + it "should compile the input file" do + pending "Can't find the sclpl header" + expect(cli(['--object', 'spec/src/sample.scl'])).to eq("") + expect(File.exists? 'spec/src/sample.o').to be(true) + FileUtils.rm('spec/src/sample.o') + end + end +end + diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ea3dcaa..409f27a 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,10 +1,15 @@ require 'open3' -def lexer(input) - out, err, status = Open3.capture3('./build/bin/sclpl-test', '--tokens', :stdin_data => input) - raise "Lexer command returned non-zero status" unless status.success? +def cli(options, input = "") + out, err, status = Open3.capture3( + *(['./build/bin/sclpl-test'] + options + [{:stdin_data => input}])) raise err unless err == "" - out.scan(/^(T_[A-Z]+(:("[^"]*"|[^\n]+))?)/m).map {|m| m[0] } + raise "Command returned non-zero status" unless status.success? + out +end + +def lexer(input) + cli(['--tokens'], input).scan(/^(T_[A-Z]+(:("[^"]*"|[^\n]+))?)/m).map {|m| m[0] } end def re_structure( token_array, offset = 0 ) @@ -25,9 +30,7 @@ def re_structure( token_array, offset = 0 ) end def ast(input) - out, err, status = Open3.capture3('./build/bin/sclpl-test', '--ast', :stdin_data => input) - raise err unless err == "" - raise "Parser command returned non-zero status" unless status.success? + out = cli(['--ast'], input) # Prep the parens for reading out.gsub!(/([()])|tree/,' \1 ') # Replace string literals so we can tokenize on spaces @@ -50,8 +53,5 @@ def ast(input) end def ccode(input) - out, err, status = Open3.capture3('./build/bin/sclpl-test', '--csource', :stdin_data => input) - raise err unless err == "" - raise "Parser command returned non-zero status" unless status.success? - out + cli(['--csource'], input) end diff --git a/spec/src/sample.scl b/spec/src/sample.scl new file mode 100644 index 0000000..e69de29 -- 2.54.0