Add spec support for C tests

This commit is contained in:
Josh Holtrop 2023-08-20 16:38:35 -04:00
parent 9d2b3be20b
commit c7185edef0

View File

@ -11,7 +11,7 @@ describe Propane do
def build_parser(options = {}) def build_parser(options = {})
options[:name] ||= "" options[:name] ||= ""
command = %W[./propane.sh spec/run/testparser#{options[:name]}.propane spec/run/testparser#{options[:name]}.d --log spec/run/testparser#{options[:name]}.log] command = %W[./propane.sh spec/run/testparser#{options[:name]}.propane spec/run/testparser#{options[:name]}.#{options[:language]} --log spec/run/testparser#{options[:name]}.log]
if (options[:capture]) if (options[:capture])
stdout, stderr, status = Open3.capture3(*command) stdout, stderr, status = Open3.capture3(*command)
Results.new(stdout, stderr, status) Results.new(stdout, stderr, status)
@ -25,9 +25,14 @@ describe Propane do
test_files = Array(test_files) test_files = Array(test_files)
options[:parsers] ||= [""] options[:parsers] ||= [""]
parsers = options[:parsers].map do |name| parsers = options[:parsers].map do |name|
"spec/run/testparser#{name}.d" "spec/run/testparser#{name}.#{options[:language]}"
end
case options[:language]
when "d"
result = system(*%w[ldc2 --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d")
when "c"
result = system(*%w[gcc -o spec/run/testparser -Ispec], *parsers, *test_files)
end end
result = system(*%w[ldc2 --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d")
expect(result).to be_truthy expect(result).to be_truthy
end end
@ -69,8 +74,12 @@ describe Propane do
FileUtils.mkdir_p("spec/run") FileUtils.mkdir_p("spec/run")
end end
it "generates a lexer" do %w[d c].each do |language|
write_grammar <<EOF
context "#{language.upcase} language" do
it "generates a lexer" do
write_grammar <<EOF
token int /\\d+/; token int /\\d+/;
token plus /\\+/; token plus /\\+/;
token times /\\*/; token times /\\*/;
@ -81,15 +90,15 @@ Foo -> int <<
Foo -> plus << Foo -> plus <<
>> >>
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_lexer.d") compile("spec/test_lexer.#{language}", language: language)
results = run results = run
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "detects a lexer error when an unknown character is seen" do it "detects a lexer error when an unknown character is seen" do
write_grammar <<EOF write_grammar <<EOF
ptype int; ptype int;
token int /\\d+/ << token int /\\d+/ <<
int v; int v;
@ -104,15 +113,15 @@ Start -> int <<
$$ = $1; $$ = $1;
>> >>
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_lexer_unknown_character.d") compile("spec/test_lexer_unknown_character.#{language}", language: language)
results = run results = run
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "generates a parser" do it "generates a parser" do
write_grammar <<EOF write_grammar <<EOF
token plus /\\+/; token plus /\\+/;
token times /\\*/; token times /\\*/;
token zero /0/; token zero /0/;
@ -124,11 +133,11 @@ E -> B;
B -> zero; B -> zero;
B -> one; B -> one;
EOF EOF
build_parser build_parser(language: language)
end end
it "generates a parser that does basic math - user guide example" do it "generates a parser that does basic math - user guide example" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.math; import std.math;
>> >>
@ -179,25 +188,25 @@ E4 -> lparen E1 rparen <<
$$ = $2; $$ = $2;
>> >>
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_basic_math_grammar.d") compile("spec/test_basic_math_grammar.#{language}", language: language)
results = run results = run
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "generates an SLR parser" do it "generates an SLR parser" do
write_grammar <<EOF write_grammar <<EOF
token one /1/; token one /1/;
Start -> E; Start -> E;
E -> one E; E -> one E;
E -> one; E -> one;
EOF EOF
build_parser build_parser(language: language)
end end
it "distinguishes between multiple identical rules with lookahead symbol" do it "distinguishes between multiple identical rules with lookahead symbol" do
write_grammar <<EOF write_grammar <<EOF
token a; token a;
token b; token b;
Start -> R1 a; Start -> R1 a;
@ -205,14 +214,14 @@ Start -> R2 b;
R1 -> a b; R1 -> a b;
R2 -> a b; R2 -> a b;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_parser_identical_rules_lookahead.d") compile("spec/test_parser_identical_rules_lookahead.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "handles reducing a rule that could be arrived at from multiple states" do it "handles reducing a rule that could be arrived at from multiple states" do
write_grammar <<EOF write_grammar <<EOF
token a; token a;
token b; token b;
drop /\\s+/; drop /\\s+/;
@ -220,14 +229,14 @@ Start -> a R1;
Start -> b R1; Start -> b R1;
R1 -> b; R1 -> b;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_parser_rule_from_multiple_states.d") compile("spec/test_parser_rule_from_multiple_states.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "executes user code when matching lexer token" do it "executes user code when matching lexer token" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.stdio; import std.stdio;
>> >>
@ -239,21 +248,21 @@ Start -> Abcs def;
Abcs -> ; Abcs -> ;
Abcs -> abc Abcs; Abcs -> abc Abcs;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_user_code.d") compile("spec/test_user_code.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
verify_lines(results.stdout, [ verify_lines(results.stdout, [
"abc!", "abc!",
"pass1", "pass1",
"abc!", "abc!",
"abc!", "abc!",
"pass2", "pass2",
]) ])
end end
it "supports a pattern statement" do it "supports a pattern statement" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.stdio; import std.stdio;
>> >>
@ -263,21 +272,21 @@ token abc;
>> >>
Start -> abc; Start -> abc;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_pattern.d") compile("spec/test_pattern.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
verify_lines(results.stdout, [ verify_lines(results.stdout, [
"def!", "def!",
"pass1", "pass1",
"def!", "def!",
"def!", "def!",
"pass2", "pass2",
]) ])
end end
it "supports returning tokens from pattern code blocks" do it "supports returning tokens from pattern code blocks" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.stdio; import std.stdio;
>> >>
@ -291,19 +300,19 @@ token abc;
>> >>
Start -> abc; Start -> abc;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_return_token_from_pattern.d") compile("spec/test_return_token_from_pattern.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
verify_lines(results.stdout, [ verify_lines(results.stdout, [
"def!", "def!",
"ghi!", "ghi!",
"def!", "def!",
]) ])
end end
it "supports lexer modes" do it "supports lexer modes" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.stdio; import std.stdio;
>> >>
@ -324,22 +333,22 @@ string: /"/ <<
>> >>
Start -> abc string def; Start -> abc string def;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_lexer_modes.d") compile("spec/test_lexer_modes.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
verify_lines(results.stdout, [ verify_lines(results.stdout, [
"begin string mode", "begin string mode",
"captured string", "captured string",
"pass1", "pass1",
"begin string mode", "begin string mode",
"captured string", "captured string",
"pass2", "pass2",
]) ])
end end
it "executes user code associated with a parser rule" do it "executes user code associated with a parser rule" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.stdio; import std.stdio;
>> >>
@ -355,19 +364,19 @@ B -> b <<
writeln("B!"); writeln("B!");
>> >>
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_parser_rule_user_code.d") compile("spec/test_parser_rule_user_code.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
verify_lines(results.stdout, [ verify_lines(results.stdout, [
"A!", "A!",
"B!", "B!",
"Start!", "Start!",
]) ])
end end
it "parses lists" do it "parses lists" do
write_grammar <<EOF write_grammar <<EOF
ptype uint; ptype uint;
token a; token a;
Start -> As << Start -> As <<
@ -380,15 +389,15 @@ As -> As a <<
$$ = $1 + 1u; $$ = $1 + 1u;
>> >>
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_parsing_lists.d") compile("spec/test_parsing_lists.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
end end
it "fails to generate a parser for a LR(1) grammar that is not LALR" do it "fails to generate a parser for a LR(1) grammar that is not LALR" do
write_grammar <<EOF write_grammar <<EOF
token a; token a;
token b; token b;
token c; token c;
@ -401,13 +410,13 @@ Start -> b E d;
E -> e; E -> e;
F -> e; F -> e;
EOF EOF
results = build_parser(capture: true) results = build_parser(capture: true, language: language)
expect(results.status).to_not eq 0 expect(results.status).to_not eq 0
expect(results.stderr).to match %r{reduce/reduce conflict.*\(E\).*\(F\)} expect(results.stderr).to match %r{reduce/reduce conflict.*\(E\).*\(F\)}
end end
it "provides matched text to user code blocks" do it "provides matched text to user code blocks" do
write_grammar <<EOF write_grammar <<EOF
<< <<
import std.stdio; import std.stdio;
>> >>
@ -416,18 +425,18 @@ token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
>> >>
Start -> id; Start -> id;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_lexer_match_text.d") compile("spec/test_lexer_match_text.#{language}", language: language)
results = run results = run
expect(results.status).to eq 0 expect(results.status).to eq 0
verify_lines(results.stdout, [ verify_lines(results.stdout, [
"Matched token is identifier_123", "Matched token is identifier_123",
"pass1", "pass1",
]) ])
end end
it "allows storing a result value for the lexer" do it "allows storing a result value for the lexer" do
write_grammar <<EOF write_grammar <<EOF
ptype ulong; ptype ulong;
token word /[a-z]+/ << token word /[a-z]+/ <<
$$ = match.length; $$ = match.length;
@ -436,53 +445,55 @@ Start -> word <<
$$ = $1; $$ = $1;
>> >>
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_lexer_result_value.d") compile("spec/test_lexer_result_value.#{language}", language: language)
results = run results = run
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "tracks position of parser errors" do it "tracks position of parser errors" do
write_grammar <<EOF write_grammar <<EOF
token a; token a;
token num /\\d+/; token num /\\d+/;
drop /\\s+/; drop /\\s+/;
Start -> a num Start; Start -> a num Start;
Start -> a num; Start -> a num;
EOF EOF
build_parser build_parser(language: language)
compile("spec/test_error_positions.d") compile("spec/test_error_positions.#{language}", language: language)
results = run results = run
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end end
it "allows creating a JSON parser" do it "allows creating a JSON parser" do
write_grammar(File.read("spec/json_parser.propane")) write_grammar(File.read("spec/json_parser.propane"))
build_parser build_parser(language: language)
compile(["spec/test_parsing_json.d", "spec/json_types.d"]) compile(["spec/test_parsing_json.#{language}", "spec/json_types.#{language}"], language: language)
end end
it "allows generating multiple parsers in the same program" do it "allows generating multiple parsers in the same program" do
write_grammar(<<EOF, name: "myp1") write_grammar(<<EOF, name: "myp1")
prefix myp1_; prefix myp1_;
token a; token a;
token num /\\d+/; token num /\\d+/;
drop /\\s+/; drop /\\s+/;
Start -> a num; Start -> a num;
EOF EOF
build_parser(name: "myp1") build_parser(name: "myp1", language: language)
write_grammar(<<EOF, name: "myp2") write_grammar(<<EOF, name: "myp2")
prefix myp2_; prefix myp2_;
token b; token b;
token c; token c;
Start -> b c b; Start -> b c b;
EOF EOF
build_parser(name: "myp2") build_parser(name: "myp2", language: language)
compile("spec/test_multiple_parsers.d", parsers: %w[myp1 myp2]) compile("spec/test_multiple_parsers.#{language}", parsers: %w[myp1 myp2], language: language)
results = run results = run
expect(results.stderr).to eq "" expect(results.stderr).to eq ""
expect(results.status).to eq 0 expect(results.status).to eq 0
end
end
end end
end end