Add C backend - close #4
This commit is contained in:
1 parent
fff9700f91
commit
3c8794058f
30 files changed
+2292
-190
No files matched your search
@@ -0,0 +1,183 @@
|
||||
<<header
|
||||
#include "json_types.h"
|
||||
#include "testutils.h"
|
||||
>>
|
||||
<<
|
||||
#include "math.h"
|
||||
#include <stdbool.h>
|
||||
static str_t string_value;
|
||||
>>
|
||||
|
||||
ptype JSONValue *;
|
||||
|
||||
drop /\s+/;
|
||||
token lbrace /\{/;
|
||||
token rbrace /\}/;
|
||||
token lbracket /\[/;
|
||||
token rbracket /\]/;
|
||||
token comma /,/;
|
||||
token colon /:/;
|
||||
token number /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?/ <<
|
||||
double n = 0.0;
|
||||
bool negative = false;
|
||||
size_t i = 0u;
|
||||
if (match[i] == '-')
|
||||
{
|
||||
negative = true;
|
||||
i++;
|
||||
}
|
||||
while ('0' <= match[i] && match[i] <= '9')
|
||||
{
|
||||
n *= 10.0;
|
||||
n += (match[i] - '0');
|
||||
i++;
|
||||
}
|
||||
if (match[i] == '.')
|
||||
{
|
||||
i++;
|
||||
double mult = 0.1;
|
||||
while ('0' <= match[i] && match[i] <= '9')
|
||||
{
|
||||
n += mult * (match[i] - '0');
|
||||
mult /= 10.0;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
if (match[i] == 'e' || match[i] == 'E')
|
||||
{
|
||||
bool exp_negative = false;
|
||||
i++;
|
||||
if (match[i] == '-')
|
||||
{
|
||||
exp_negative = true;
|
||||
i++;
|
||||
}
|
||||
else if (match[i] == '+')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
long exp = 0.0;
|
||||
while ('0' <= match[i] && match[i] <= '9')
|
||||
{
|
||||
exp *= 10;
|
||||
exp += (match[i] - '0');
|
||||
i++;
|
||||
}
|
||||
if (exp_negative)
|
||||
{
|
||||
exp = -exp;
|
||||
}
|
||||
n = pow(n, exp);
|
||||
}
|
||||
if (negative)
|
||||
{
|
||||
n = -n;
|
||||
}
|
||||
$$ = JSONValue_new(JSON_NUMBER);
|
||||
$$->number = n;
|
||||
>>
|
||||
token true <<
|
||||
$$ = JSONValue_new(JSON_TRUE);
|
||||
>>
|
||||
token false <<
|
||||
$$ = JSONValue_new(JSON_FALSE);
|
||||
>>
|
||||
token null <<
|
||||
$$ = JSONValue_new(JSON_NULL);
|
||||
>>
|
||||
/"/ <<
|
||||
$mode(string);
|
||||
str_init(&string_value, "");
|
||||
>>
|
||||
string: token string /"/ <<
|
||||
$$ = JSONValue_new(JSON_STRING);
|
||||
$$->string = string_value;
|
||||
$mode(default);
|
||||
>>
|
||||
string: /\\"/ <<
|
||||
str_append(&string_value, "\"");
|
||||
>>
|
||||
string: /\\\\/ <<
|
||||
str_append(&string_value, "\\");
|
||||
>>
|
||||
string: /\\\// <<
|
||||
str_append(&string_value, "/");
|
||||
>>
|
||||
string: /\\b/ <<
|
||||
str_append(&string_value, "\b");
|
||||
>>
|
||||
string: /\\f/ <<
|
||||
str_append(&string_value, "\f");
|
||||
>>
|
||||
string: /\\n/ <<
|
||||
str_append(&string_value, "\n");
|
||||
>>
|
||||
string: /\\r/ <<
|
||||
str_append(&string_value, "\r");
|
||||
>>
|
||||
string: /\\t/ <<
|
||||
str_append(&string_value, "\t");
|
||||
>>
|
||||
string: /\\u[0-9a-fA-F]{4}/ <<
|
||||
/* Not actually going to encode the code point for this example... */
|
||||
char s[] = {'{', match[2], match[3], match[4], match[5], '}', 0};
|
||||
str_append(&string_value, s);
|
||||
>>
|
||||
string: /[^\\]/ <<
|
||||
char s[] = {match[0], 0};
|
||||
str_append(&string_value, s);
|
||||
>>
|
||||
Start -> Value <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> string <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> number <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> Object <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> Array <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> true <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> false <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Value -> null <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Object -> lbrace rbrace <<
|
||||
$$ = JSONObject_new();
|
||||
>>
|
||||
Object -> lbrace KeyValues rbrace <<
|
||||
$$ = $2;
|
||||
>>
|
||||
KeyValues -> KeyValue <<
|
||||
$$ = $1;
|
||||
>>
|
||||
KeyValues -> KeyValues comma KeyValue <<
|
||||
JSONObject_append($1, $3->object.entries[0].name, $3->object.entries[0].value);
|
||||
$$ = $1;
|
||||
>>
|
||||
KeyValue -> string colon Value <<
|
||||
$$ = JSONObject_new();
|
||||
JSONObject_append($$, str_cstr(&$1->string), $3);
|
||||
>>
|
||||
Array -> lbracket rbracket <<
|
||||
$$ = JSONArray_new();
|
||||
>>
|
||||
Array -> lbracket Values rbracket <<
|
||||
$$ = $2;
|
||||
>>
|
||||
Values -> Value <<
|
||||
$$ = $1;
|
||||
>>
|
||||
Values -> Values comma Value <<
|
||||
JSONArray_append($1, $3);
|
||||
$$ = $1;
|
||||
>>
|
||||
File renamed without changes.
@@ -0,0 +1,64 @@
|
||||
#include "json_types.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "testutils.h"
|
||||
|
||||
JSONValue * JSONValue_new(size_t id)
|
||||
{
|
||||
JSONValue * jv = calloc(1, sizeof(JSONValue));
|
||||
jv->id = id;
|
||||
return jv;
|
||||
}
|
||||
|
||||
JSONValue * JSONObject_new(void)
|
||||
{
|
||||
JSONValue * jv = JSONValue_new(JSON_OBJECT);
|
||||
jv->object.size = 0u;
|
||||
return jv;
|
||||
}
|
||||
|
||||
void JSONObject_append(JSONValue * object, char const * name, JSONValue * value)
|
||||
{
|
||||
size_t const size = object->object.size;
|
||||
for (size_t i = 0u; i < size; i++)
|
||||
{
|
||||
if (strcmp(name, object->object.entries[i].name) == 0)
|
||||
{
|
||||
object->object.entries[i].value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
size_t const new_size = size + 1;
|
||||
void * new_entries = malloc(sizeof(object->object.entries[0]) * new_size);
|
||||
if (size > 0)
|
||||
{
|
||||
memcpy(new_entries, object->object.entries, size * sizeof(object->object.entries[0]));
|
||||
free(object->object.entries);
|
||||
}
|
||||
object->object.entries = new_entries;
|
||||
object->object.entries[size].name = name;
|
||||
object->object.entries[size].value = value;
|
||||
object->object.size = new_size;
|
||||
}
|
||||
|
||||
JSONValue * JSONArray_new(void)
|
||||
{
|
||||
JSONValue * jv = JSONValue_new(JSON_ARRAY);
|
||||
jv->array.size = 0u;
|
||||
return jv;
|
||||
}
|
||||
|
||||
void JSONArray_append(JSONValue * array, JSONValue * value)
|
||||
{
|
||||
size_t const size = array->array.size;
|
||||
size_t const new_size = size + 1;
|
||||
JSONValue ** new_entries = malloc(sizeof(JSONValue *) * new_size);
|
||||
if (array->array.size > 0)
|
||||
{
|
||||
memcpy(new_entries, array->array.entries, sizeof(JSONValue *) * size);
|
||||
free(array->array.entries);
|
||||
}
|
||||
array->array.entries = new_entries;
|
||||
array->array.entries[size] = value;
|
||||
array->array.size = new_size;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "testutils.h"
|
||||
|
||||
#define JSON_OBJECT 0u
|
||||
#define JSON_ARRAY 1u
|
||||
#define JSON_NUMBER 2u
|
||||
#define JSON_STRING 3u
|
||||
#define JSON_TRUE 4u
|
||||
#define JSON_FALSE 5u
|
||||
#define JSON_NULL 6u
|
||||
|
||||
typedef struct JSONValue_s
|
||||
{
|
||||
size_t id;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
size_t size;
|
||||
struct
|
||||
{
|
||||
char const * name;
|
||||
struct JSONValue_s * value;
|
||||
} * entries;
|
||||
} object;
|
||||
struct
|
||||
{
|
||||
size_t size;
|
||||
struct JSONValue_s ** entries;
|
||||
} array;
|
||||
double number;
|
||||
str_t string;
|
||||
};
|
||||
} JSONValue;
|
||||
|
||||
JSONValue * JSONValue_new(size_t id);
|
||||
|
||||
JSONValue * JSONObject_new(void);
|
||||
|
||||
void JSONObject_append(JSONValue * object, char const * name, JSONValue * value);
|
||||
|
||||
JSONValue * JSONArray_new(void);
|
||||
|
||||
void JSONArray_append(JSONValue * array, JSONValue * value);
|
||||
+380
-172
@@ -11,7 +11,7 @@ describe Propane do
|
||||
|
||||
def build_parser(options = {})
|
||||
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])
|
||||
stdout, stderr, status = Open3.capture3(*command)
|
||||
Results.new(stdout, stderr, status)
|
||||
@@ -25,9 +25,14 @@ describe Propane do
|
||||
test_files = Array(test_files)
|
||||
options[:parsers] ||= [""]
|
||||
parsers = options[:parsers].map do |name|
|
||||
"spec/run/testparser#{name}.d"
|
||||
"spec/run/testparser#{name}.#{options[:language]}"
|
||||
end
|
||||
case options[:language]
|
||||
when "c"
|
||||
result = system(*%w[gcc -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm")
|
||||
when "d"
|
||||
result = system(*%w[ldc2 --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d")
|
||||
end
|
||||
result = system(*%w[ldc2 --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d")
|
||||
expect(result).to be_truthy
|
||||
end
|
||||
|
||||
@@ -69,8 +74,12 @@ describe Propane do
|
||||
FileUtils.mkdir_p("spec/run")
|
||||
end
|
||||
|
||||
it "generates a lexer" do
|
||||
write_grammar <<EOF
|
||||
%w[d c].each do |language|
|
||||
|
||||
context "#{language.upcase} language" do
|
||||
|
||||
it "generates a lexer" do
|
||||
write_grammar <<EOF
|
||||
token int /\\d+/;
|
||||
token plus /\\+/;
|
||||
token times /\\*/;
|
||||
@@ -81,15 +90,33 @@ Foo -> int <<
|
||||
Foo -> plus <<
|
||||
>>
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_lexer.d")
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_lexer.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "detects a lexer error when an unknown character is seen" do
|
||||
write_grammar <<EOF
|
||||
it "detects a lexer error when an unknown character is seen" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
ptype int;
|
||||
token int /\\d+/ <<
|
||||
int v = 0;
|
||||
for (size_t i = 0u; i < match_length; i++)
|
||||
{
|
||||
v *= 10;
|
||||
v += (match[i] - '0');
|
||||
}
|
||||
$$ = v;
|
||||
>>
|
||||
Start -> int <<
|
||||
$$ = $1;
|
||||
>>
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
ptype int;
|
||||
token int /\\d+/ <<
|
||||
int v;
|
||||
@@ -104,15 +131,16 @@ Start -> int <<
|
||||
$$ = $1;
|
||||
>>
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_lexer_unknown_character.d")
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_lexer_unknown_character.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "generates a parser" do
|
||||
write_grammar <<EOF
|
||||
it "generates a parser" do
|
||||
write_grammar <<EOF
|
||||
token plus /\\+/;
|
||||
token times /\\*/;
|
||||
token zero /0/;
|
||||
@@ -124,11 +152,65 @@ E -> B;
|
||||
B -> zero;
|
||||
B -> one;
|
||||
EOF
|
||||
build_parser
|
||||
end
|
||||
build_parser(language: language)
|
||||
end
|
||||
|
||||
it "generates a parser that does basic math - user guide example" do
|
||||
write_grammar <<EOF
|
||||
it "generates a parser that does basic math - user guide example" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <math.h>
|
||||
>>
|
||||
|
||||
ptype size_t;
|
||||
|
||||
token plus /\\+/;
|
||||
token times /\\*/;
|
||||
token power /\\*\\*/;
|
||||
token integer /\\d+/ <<
|
||||
size_t v = 0u;
|
||||
for (size_t i = 0u; i < match_length; i++)
|
||||
{
|
||||
v *= 10;
|
||||
v += (match[i] - '0');
|
||||
}
|
||||
$$ = v;
|
||||
>>
|
||||
token lparen /\\(/;
|
||||
token rparen /\\)/;
|
||||
drop /\\s+/;
|
||||
|
||||
Start -> E1 <<
|
||||
$$ = $1;
|
||||
>>
|
||||
E1 -> E2 <<
|
||||
$$ = $1;
|
||||
>>
|
||||
E1 -> E1 plus E2 <<
|
||||
$$ = $1 + $3;
|
||||
>>
|
||||
E2 -> E3 <<
|
||||
$$ = $1;
|
||||
>>
|
||||
E2 -> E2 times E3 <<
|
||||
$$ = $1 * $3;
|
||||
>>
|
||||
E3 -> E4 <<
|
||||
$$ = $1;
|
||||
>>
|
||||
E3 -> E3 power E4 <<
|
||||
$$ = (size_t)pow($1, $3);
|
||||
>>
|
||||
E4 -> integer <<
|
||||
$$ = $1;
|
||||
>>
|
||||
E4 -> lparen E1 rparen <<
|
||||
$$ = $2;
|
||||
>>
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.math;
|
||||
>>
|
||||
@@ -179,25 +261,26 @@ E4 -> lparen E1 rparen <<
|
||||
$$ = $2;
|
||||
>>
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_basic_math_grammar.d")
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_basic_math_grammar.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "generates an SLR parser" do
|
||||
write_grammar <<EOF
|
||||
it "generates an SLR parser" do
|
||||
write_grammar <<EOF
|
||||
token one /1/;
|
||||
Start -> E;
|
||||
E -> one E;
|
||||
E -> one;
|
||||
EOF
|
||||
build_parser
|
||||
end
|
||||
build_parser(language: language)
|
||||
end
|
||||
|
||||
it "distinguishes between multiple identical rules with lookahead symbol" do
|
||||
write_grammar <<EOF
|
||||
it "distinguishes between multiple identical rules with lookahead symbol" do
|
||||
write_grammar <<EOF
|
||||
token a;
|
||||
token b;
|
||||
Start -> R1 a;
|
||||
@@ -205,14 +288,14 @@ Start -> R2 b;
|
||||
R1 -> a b;
|
||||
R2 -> a b;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_parser_identical_rules_lookahead.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_parser_identical_rules_lookahead.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "handles reducing a rule that could be arrived at from multiple states" do
|
||||
write_grammar <<EOF
|
||||
it "handles reducing a rule that could be arrived at from multiple states" do
|
||||
write_grammar <<EOF
|
||||
token a;
|
||||
token b;
|
||||
drop /\\s+/;
|
||||
@@ -220,14 +303,29 @@ Start -> a R1;
|
||||
Start -> b R1;
|
||||
R1 -> b;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_parser_rule_from_multiple_states.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_parser_rule_from_multiple_states.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "executes user code when matching lexer token" do
|
||||
write_grammar <<EOF
|
||||
it "executes user code when matching lexer token" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <stdio.h>
|
||||
>>
|
||||
token abc <<
|
||||
printf("abc!\\n");
|
||||
>>
|
||||
token def;
|
||||
Start -> Abcs def;
|
||||
Abcs -> ;
|
||||
Abcs -> abc Abcs;
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.stdio;
|
||||
>>
|
||||
@@ -239,21 +337,35 @@ Start -> Abcs def;
|
||||
Abcs -> ;
|
||||
Abcs -> abc Abcs;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_user_code.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"abc!",
|
||||
"pass1",
|
||||
"abc!",
|
||||
"abc!",
|
||||
"pass2",
|
||||
])
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_user_code.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"abc!",
|
||||
"pass1",
|
||||
"abc!",
|
||||
"abc!",
|
||||
"pass2",
|
||||
])
|
||||
end
|
||||
|
||||
it "supports a pattern statement" do
|
||||
write_grammar <<EOF
|
||||
it "supports a pattern statement" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <stdio.h>
|
||||
>>
|
||||
token abc;
|
||||
/def/ <<
|
||||
printf("def!\\n");
|
||||
>>
|
||||
Start -> abc;
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.stdio;
|
||||
>>
|
||||
@@ -263,21 +375,39 @@ token abc;
|
||||
>>
|
||||
Start -> abc;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_pattern.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"def!",
|
||||
"pass1",
|
||||
"def!",
|
||||
"def!",
|
||||
"pass2",
|
||||
])
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_pattern.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"def!",
|
||||
"pass1",
|
||||
"def!",
|
||||
"def!",
|
||||
"pass2",
|
||||
])
|
||||
end
|
||||
|
||||
it "supports returning tokens from pattern code blocks" do
|
||||
write_grammar <<EOF
|
||||
it "supports returning tokens from pattern code blocks" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <stdio.h>
|
||||
>>
|
||||
token abc;
|
||||
/def/ <<
|
||||
printf("def!\\n");
|
||||
>>
|
||||
/ghi/ <<
|
||||
printf("ghi!\\n");
|
||||
return $token(abc);
|
||||
>>
|
||||
Start -> abc;
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.stdio;
|
||||
>>
|
||||
@@ -291,19 +421,44 @@ token abc;
|
||||
>>
|
||||
Start -> abc;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_return_token_from_pattern.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"def!",
|
||||
"ghi!",
|
||||
"def!",
|
||||
])
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_return_token_from_pattern.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"def!",
|
||||
"ghi!",
|
||||
"def!",
|
||||
])
|
||||
end
|
||||
|
||||
it "supports lexer modes" do
|
||||
write_grammar <<EOF
|
||||
it "supports lexer modes" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <stdio.h>
|
||||
>>
|
||||
token abc;
|
||||
token def;
|
||||
tokenid string;
|
||||
drop /\\s+/;
|
||||
/"/ <<
|
||||
printf("begin string mode\\n");
|
||||
$mode(string);
|
||||
>>
|
||||
string: /[^"]+/ <<
|
||||
printf("captured string\\n");
|
||||
>>
|
||||
string: /"/ <<
|
||||
$mode(default);
|
||||
return $token(string);
|
||||
>>
|
||||
Start -> abc string def;
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.stdio;
|
||||
>>
|
||||
@@ -324,22 +479,42 @@ string: /"/ <<
|
||||
>>
|
||||
Start -> abc string def;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_lexer_modes.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"begin string mode",
|
||||
"captured string",
|
||||
"pass1",
|
||||
"begin string mode",
|
||||
"captured string",
|
||||
"pass2",
|
||||
])
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_lexer_modes.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"begin string mode",
|
||||
"captured string",
|
||||
"pass1",
|
||||
"begin string mode",
|
||||
"captured string",
|
||||
"pass2",
|
||||
])
|
||||
end
|
||||
|
||||
it "executes user code associated with a parser rule" do
|
||||
write_grammar <<EOF
|
||||
it "executes user code associated with a parser rule" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <stdio.h>
|
||||
>>
|
||||
token a;
|
||||
token b;
|
||||
Start -> A B <<
|
||||
printf("Start!\\n");
|
||||
>>
|
||||
A -> a <<
|
||||
printf("A!\\n");
|
||||
>>
|
||||
B -> b <<
|
||||
printf("B!\\n");
|
||||
>>
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.stdio;
|
||||
>>
|
||||
@@ -355,20 +530,21 @@ B -> b <<
|
||||
writeln("B!");
|
||||
>>
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_parser_rule_user_code.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"A!",
|
||||
"B!",
|
||||
"Start!",
|
||||
])
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_parser_rule_user_code.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"A!",
|
||||
"B!",
|
||||
"Start!",
|
||||
])
|
||||
end
|
||||
|
||||
it "parses lists" do
|
||||
write_grammar <<EOF
|
||||
ptype uint;
|
||||
it "parses lists" do
|
||||
write_grammar <<EOF
|
||||
ptype #{language == "c" ? "uint32_t" : "uint"};
|
||||
token a;
|
||||
Start -> As <<
|
||||
$$ = $1;
|
||||
@@ -380,15 +556,15 @@ As -> As a <<
|
||||
$$ = $1 + 1u;
|
||||
>>
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_parsing_lists.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
expect(results.stderr).to eq ""
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_parsing_lists.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
expect(results.stderr).to eq ""
|
||||
end
|
||||
|
||||
it "fails to generate a parser for a LR(1) grammar that is not LALR" do
|
||||
write_grammar <<EOF
|
||||
it "fails to generate a parser for a LR(1) grammar that is not LALR" do
|
||||
write_grammar <<EOF
|
||||
token a;
|
||||
token b;
|
||||
token c;
|
||||
@@ -401,13 +577,29 @@ Start -> b E d;
|
||||
E -> e;
|
||||
F -> e;
|
||||
EOF
|
||||
results = build_parser(capture: true)
|
||||
expect(results.status).to_not eq 0
|
||||
expect(results.stderr).to match %r{reduce/reduce conflict.*\(E\).*\(F\)}
|
||||
end
|
||||
results = build_parser(capture: true, language: language)
|
||||
expect(results.status).to_not eq 0
|
||||
expect(results.stderr).to match %r{reduce/reduce conflict.*\(E\).*\(F\)}
|
||||
end
|
||||
|
||||
it "provides matched text to user code blocks" do
|
||||
write_grammar <<EOF
|
||||
it "provides matched text to user code blocks" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
>>
|
||||
token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
|
||||
char * t = malloc(match_length + 1);
|
||||
strncpy(t, (char *)match, match_length);
|
||||
printf("Matched token is %s\\n", t);
|
||||
free(t);
|
||||
>>
|
||||
Start -> id;
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
<<
|
||||
import std.stdio;
|
||||
>>
|
||||
@@ -416,18 +608,31 @@ token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
|
||||
>>
|
||||
Start -> id;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_lexer_match_text.d")
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"Matched token is identifier_123",
|
||||
"pass1",
|
||||
])
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_lexer_match_text.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.status).to eq 0
|
||||
verify_lines(results.stdout, [
|
||||
"Matched token is identifier_123",
|
||||
"pass1",
|
||||
])
|
||||
end
|
||||
|
||||
it "allows storing a result value for the lexer" do
|
||||
write_grammar <<EOF
|
||||
it "allows storing a result value for the lexer" do
|
||||
case language
|
||||
when "c"
|
||||
write_grammar <<EOF
|
||||
ptype uint64_t;
|
||||
token word /[a-z]+/ <<
|
||||
$$ = match_length;
|
||||
>>
|
||||
Start -> word <<
|
||||
$$ = $1;
|
||||
>>
|
||||
EOF
|
||||
when "d"
|
||||
write_grammar <<EOF
|
||||
ptype ulong;
|
||||
token word /[a-z]+/ <<
|
||||
$$ = match.length;
|
||||
@@ -436,53 +641,56 @@ Start -> word <<
|
||||
$$ = $1;
|
||||
>>
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_lexer_result_value.d")
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_lexer_result_value.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "tracks position of parser errors" do
|
||||
write_grammar <<EOF
|
||||
it "tracks position of parser errors" do
|
||||
write_grammar <<EOF
|
||||
token a;
|
||||
token num /\\d+/;
|
||||
drop /\\s+/;
|
||||
Start -> a num Start;
|
||||
Start -> a num;
|
||||
EOF
|
||||
build_parser
|
||||
compile("spec/test_error_positions.d")
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
build_parser(language: language)
|
||||
compile("spec/test_error_positions.#{language}", language: language)
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "allows creating a JSON parser" do
|
||||
write_grammar(File.read("spec/json_parser.propane"))
|
||||
build_parser
|
||||
compile(["spec/test_parsing_json.d", "spec/json_types.d"])
|
||||
end
|
||||
it "allows creating a JSON parser" do
|
||||
write_grammar(File.read("spec/json_parser.#{language}.propane"))
|
||||
build_parser(language: language)
|
||||
compile(["spec/test_parsing_json.#{language}", "spec/json_types.#{language}"], language: language)
|
||||
end
|
||||
|
||||
it "allows generating multiple parsers in the same program" do
|
||||
write_grammar(<<EOF, name: "myp1")
|
||||
it "allows generating multiple parsers in the same program" do
|
||||
write_grammar(<<EOF, name: "myp1")
|
||||
prefix myp1_;
|
||||
token a;
|
||||
token num /\\d+/;
|
||||
drop /\\s+/;
|
||||
Start -> a num;
|
||||
EOF
|
||||
build_parser(name: "myp1")
|
||||
write_grammar(<<EOF, name: "myp2")
|
||||
build_parser(name: "myp1", language: language)
|
||||
write_grammar(<<EOF, name: "myp2")
|
||||
prefix myp2_;
|
||||
token b;
|
||||
token c;
|
||||
Start -> b c b;
|
||||
EOF
|
||||
build_parser(name: "myp2")
|
||||
compile("spec/test_multiple_parsers.d", parsers: %w[myp1 myp2])
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
build_parser(name: "myp2", language: language)
|
||||
compile("spec/test_multiple_parsers.#{language}", parsers: %w[myp1 myp2], language: language)
|
||||
results = run
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "testparser.h"
|
||||
#include "testutils.h"
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "1 + 2 * 3 + 4";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert_eq(P_SUCCESS, p_parse(&context));
|
||||
assert_eq(11, p_result(&context));
|
||||
|
||||
input = "1 * 2 ** 4 * 3";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert_eq(P_SUCCESS, p_parse(&context));
|
||||
assert_eq(48, p_result(&context));
|
||||
|
||||
input = "(1 + 2) * 3 + 4";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert_eq(P_SUCCESS, p_parse(&context));
|
||||
assert_eq(13, p_result(&context));
|
||||
|
||||
input = "(2 * 2) ** 3 + 4 + 5";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert_eq(P_SUCCESS, p_parse(&context));
|
||||
assert_eq(73, p_result(&context));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "a 42";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
input = "a\n123\na a";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_UNEXPECTED_TOKEN);
|
||||
assert(p_position(&context).row == 2);
|
||||
assert(p_position(&context).col == 3);
|
||||
assert(context.token == TOKEN_a);
|
||||
|
||||
input = "12";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_UNEXPECTED_TOKEN);
|
||||
assert(p_position(&context).row == 0);
|
||||
assert(p_position(&context).col == 0);
|
||||
assert(context.token == TOKEN_num);
|
||||
|
||||
input = "a 12\n\nab";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_UNEXPECTED_INPUT);
|
||||
assert(p_position(&context).row == 2);
|
||||
assert(p_position(&context).col == 1);
|
||||
|
||||
input = "a 12\n\na\n\n77\na \xAA";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_DECODE_ERROR);
|
||||
assert(p_position(&context).row == 5);
|
||||
assert(p_position(&context).col == 4);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -33,6 +33,5 @@ unittest
|
||||
input = "a 12\n\na\n\n77\na \xAA";
|
||||
p_context_init(&context, input);
|
||||
assert(p_parse(&context) == P_DECODE_ERROR);
|
||||
writeln(p_position(&context));
|
||||
assert(p_position(&context) == p_position_t(5, 4));
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
size_t result;
|
||||
p_code_point_t code_point;
|
||||
uint8_t code_point_length;
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"5", 1u, &code_point, &code_point_length);
|
||||
assert(result == P_SUCCESS);
|
||||
assert(code_point == '5');
|
||||
assert(code_point_length == 1u);
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"", 0u, &code_point, &code_point_length);
|
||||
assert(result == P_EOF);
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"\xC2\xA9", 2u, &code_point, &code_point_length);
|
||||
assert(result == P_SUCCESS);
|
||||
assert(code_point == 0xA9u);
|
||||
assert(code_point_length == 2u);
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"\xf0\x9f\xa7\xa1", 4u, &code_point, &code_point_length);
|
||||
assert(result == P_SUCCESS);
|
||||
assert(code_point == 0x1F9E1u);
|
||||
assert(code_point_length == 4u);
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"\xf0\x9f\x27", 3u, &code_point, &code_point_length);
|
||||
assert(result == P_DECODE_ERROR);
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"\xf0\x9f\xa7\xFF", 4u, &code_point, &code_point_length);
|
||||
assert(result == P_DECODE_ERROR);
|
||||
|
||||
result = p_decode_code_point((uint8_t const *)"\xfe", 1u, &code_point, &code_point_length);
|
||||
assert(result == P_DECODE_ERROR);
|
||||
|
||||
|
||||
p_token_info_t token_info;
|
||||
char const * input = "5 + 4 * \n677 + 567";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 0u);
|
||||
assert(token_info.position.col == 0u);
|
||||
assert(token_info.length == 1u);
|
||||
assert(token_info.token == TOKEN_int);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 0u);
|
||||
assert(token_info.position.col == 2u);
|
||||
assert(token_info.length == 1u);
|
||||
assert(token_info.token == TOKEN_plus);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 0u);
|
||||
assert(token_info.position.col == 4u);
|
||||
assert(token_info.length == 1u);
|
||||
assert(token_info.token == TOKEN_int);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 0u);
|
||||
assert(token_info.position.col == 6u);
|
||||
assert(token_info.length == 1u);
|
||||
assert(token_info.token == TOKEN_times);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 1u);
|
||||
assert(token_info.position.col == 0u);
|
||||
assert(token_info.length == 3u);
|
||||
assert(token_info.token == TOKEN_int);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 1u);
|
||||
assert(token_info.position.col == 4u);
|
||||
assert(token_info.length == 1u);
|
||||
assert(token_info.token == TOKEN_plus);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 1u);
|
||||
assert(token_info.position.col == 6u);
|
||||
assert(token_info.length == 3u);
|
||||
assert(token_info.token == TOKEN_int);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 1u);
|
||||
assert(token_info.position.col == 9u);
|
||||
assert(token_info.length == 0u);
|
||||
assert(token_info.token == TOKEN___EOF);
|
||||
|
||||
p_context_init(&context, (uint8_t const *)"", 0u);
|
||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||
assert(token_info.position.row == 0u);
|
||||
assert(token_info.position.col == 0u);
|
||||
assert(token_info.length == 0u);
|
||||
assert(token_info.token == TOKEN___EOF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "identifier_123";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass1\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "abc \"a string\" def";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass1\n");
|
||||
|
||||
input = "abc \"abc def\" def";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass2\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "x";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context) == 1u);
|
||||
|
||||
input = "fabulous";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context) == 8u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "x";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_UNEXPECTED_INPUT);
|
||||
|
||||
input = "123";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context) == 123u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include "testparsermyp1.h"
|
||||
#include "testparsermyp2.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input1 = "a\n1";
|
||||
myp1_context_t context1;
|
||||
myp1_context_init(&context1, (uint8_t const *)input1, strlen(input1));
|
||||
assert(myp1_parse(&context1) == MYP1_SUCCESS);
|
||||
|
||||
char const * input2 = "bcb";
|
||||
myp2_context_t context2;
|
||||
myp2_context_init(&context2, (uint8_t const *)input2, strlen(input2));
|
||||
assert(myp2_parse(&context2) == MYP2_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "testparser.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "aba";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
input = "abb";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "a";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_UNEXPECTED_TOKEN);
|
||||
assert(p_position(&context).row == 0);
|
||||
assert(p_position(&context).col == 1);
|
||||
assert(context.token == TOKEN___EOF);
|
||||
|
||||
input = "a b";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
input = "bb";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "ab";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "testparser.h"
|
||||
#include "json_types.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
input = "{}";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context)->id == JSON_OBJECT);
|
||||
|
||||
input = "[]";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context)->id == JSON_ARRAY);
|
||||
|
||||
input = "-45.6";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context)->id == JSON_NUMBER);
|
||||
assert(p_result(&context)->number == -45.6);
|
||||
|
||||
input = "2E-2";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context)->id == JSON_NUMBER);
|
||||
assert(p_result(&context)->number == 0.02);
|
||||
|
||||
input = "{\"hi\":true}";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
JSONValue * o = p_result(&context);
|
||||
assert(o->id == JSON_OBJECT);
|
||||
assert_eq(1, o->object.size);
|
||||
assert(strcmp(o->object.entries[0].name, "hi") == 0);
|
||||
assert(o->object.entries[0].value->id == JSON_TRUE);
|
||||
|
||||
input = "{\"ff\": false, \"nn\": null}";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
o = p_result(&context);
|
||||
assert(o->id == JSON_OBJECT);
|
||||
assert_eq(2, o->object.size);
|
||||
assert(strcmp(o->object.entries[0].name, "ff") == 0);
|
||||
assert(o->object.entries[0].value->id == JSON_FALSE);
|
||||
assert(strcmp(o->object.entries[1].name, "nn") == 0);
|
||||
assert(o->object.entries[1].value->id == JSON_NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "a";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context) == 1u);
|
||||
|
||||
input = "";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context) == 0u);
|
||||
|
||||
input = "aaaaaaaaaaaaaaaa";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
assert(p_result(&context) == 16u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "testparser.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "abcdef";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass1\n");
|
||||
|
||||
input = "defabcdef";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass2\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "defghidef";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "abcdef";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass1\n");
|
||||
|
||||
input = "abcabcdef";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
printf("pass2\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "testutils.h"
|
||||
|
||||
void assert_eq_size_t_i(size_t expected, size_t actual, char const * file, size_t line)
|
||||
{
|
||||
if (expected != actual)
|
||||
{
|
||||
fprintf(stderr, "%s:%lu: expected %lu, got %lu\n", file, line, expected, actual);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void str_init(str_t * str, char const * cs)
|
||||
{
|
||||
size_t length = strlen(cs);
|
||||
str->cs = malloc(length + 1u);
|
||||
strcpy(str->cs, cs);
|
||||
}
|
||||
|
||||
void str_append(str_t * str, char const * cs)
|
||||
{
|
||||
size_t length = strlen(str->cs);
|
||||
size_t length2 = strlen(cs);
|
||||
char * new_cs = malloc(length + length2 + 1u);
|
||||
memcpy(new_cs, str->cs, length);
|
||||
strcpy(&new_cs[length], cs);
|
||||
free(str->cs);
|
||||
str->cs = new_cs;
|
||||
}
|
||||
|
||||
void str_free(str_t * str)
|
||||
{
|
||||
free(str->cs);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
void assert_eq_size_t_i(size_t expected, size_t actual, char const * file, size_t line);
|
||||
|
||||
#define assert_eq(expected, actual) \
|
||||
assert_eq_size_t_i(expected, actual, __FILE__, __LINE__)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char * cs;
|
||||
} str_t;
|
||||
|
||||
void str_init(str_t * str, char const * cs);
|
||||
void str_append(str_t * str, char const * cs);
|
||||
void str_free(str_t * str);
|
||||
static inline char * str_cstr(str_t * str)
|
||||
{
|
||||
return str->cs;
|
||||
}
|
||||
Reference in new issue
Block a user