Compare commits
19
Commits
7f844711d9
...
sumtype
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d466189982 | ||
|
|
9895733a05 | ||
|
|
11348ca351 | ||
|
|
6b0fb4cb12 | ||
|
|
b78827746a | ||
|
|
ce80e00e60 | ||
|
|
01b45df1f9 | ||
|
|
ffccc659aa | ||
|
|
e432f62b05 | ||
|
|
058945e08b | ||
|
|
f402315201 | ||
|
|
d10e982fc8 | ||
|
|
64974cc1e2 | ||
|
|
b92679e0c2 | ||
|
|
1af018b103 | ||
|
|
aabb574fea | ||
|
|
30004c571d | ||
|
|
b00f53050b | ||
|
|
2e5083d823 |
No files matched your search
+374
-133
@@ -2,8 +2,8 @@
|
||||
module <%= @grammar.modulename %>;
|
||||
|
||||
<% end %>
|
||||
|
||||
import std.stdio;
|
||||
import std.sumtype;
|
||||
|
||||
<% @grammar.code_blocks.each do |code| %>
|
||||
<%= code %>
|
||||
@@ -11,17 +11,52 @@ import std.stdio;
|
||||
|
||||
class <%= @classname %>
|
||||
{
|
||||
enum
|
||||
alias TokenID = uint;
|
||||
|
||||
enum : TokenID
|
||||
{
|
||||
<% @grammar.tokens.each_with_index do |token, index| %>
|
||||
TOKEN_<%= token.code_name %> = <%= index %>,
|
||||
<% unless token.id == index %>
|
||||
<% raise "Token ID (#{token.id}) does not match index (#{index}) for token #{token.name}!" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
_TOKEN_COUNT = <%= @grammar.tokens.size %>,
|
||||
_TOKEN_DECODE_ERROR = <%= TOKEN_DECODE_ERROR %>,
|
||||
_TOKEN_DROP = <%= TOKEN_DROP %>,
|
||||
}
|
||||
|
||||
static immutable string token_names[] = [
|
||||
struct Token
|
||||
{
|
||||
/* Number of tokens in this parser. */
|
||||
enum count = <%= @grammar.tokens.size %>;
|
||||
TokenID token;
|
||||
alias token this;
|
||||
|
||||
@disable this();
|
||||
|
||||
this(TokenID token)
|
||||
{
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
static Token invalid()
|
||||
{
|
||||
return Token(count);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return token < count;
|
||||
}
|
||||
|
||||
bool is_invalid() const
|
||||
{
|
||||
return !is_valid();
|
||||
}
|
||||
}
|
||||
|
||||
alias CodePoint = uint;
|
||||
|
||||
static immutable string[] token_names = [
|
||||
<% @grammar.tokens.each_with_index do |token, index| %>
|
||||
"<%= token.name %>",
|
||||
<% end %>
|
||||
@@ -36,26 +71,26 @@ class <%= @classname %>
|
||||
|
||||
static class Decoder
|
||||
{
|
||||
enum
|
||||
struct Success
|
||||
{
|
||||
CODE_POINT_INVALID = 0xFFFFFFFE,
|
||||
CODE_POINT_EOF = 0xFFFFFFFF,
|
||||
}
|
||||
|
||||
struct DecodedCodePoint
|
||||
{
|
||||
uint code_point;
|
||||
CodePoint code_point;
|
||||
uint code_point_length;
|
||||
}
|
||||
struct EOF {}
|
||||
struct DecodeError {}
|
||||
|
||||
static DecodedCodePoint decode_code_point(string input)
|
||||
static SumType!(
|
||||
Success,
|
||||
EOF,
|
||||
DecodeError)
|
||||
decode_code_point(string input)
|
||||
{
|
||||
if (input.length == 0u)
|
||||
{
|
||||
return DecodedCodePoint(CODE_POINT_EOF, 0u);
|
||||
return cast(ReturnType!decode_code_point)EOF();
|
||||
}
|
||||
char c = input[0];
|
||||
uint code_point;
|
||||
CodePoint code_point;
|
||||
uint code_point_length;
|
||||
if ((c & 0x80u) == 0u)
|
||||
{
|
||||
@@ -92,11 +127,11 @@ class <%= @classname %>
|
||||
}
|
||||
else
|
||||
{
|
||||
return DecodedCodePoint(CODE_POINT_INVALID, 0u);
|
||||
return cast(ReturnType!decode_code_point)DecodeError();
|
||||
}
|
||||
if (input.length <= following_bytes)
|
||||
{
|
||||
return DecodedCodePoint(CODE_POINT_INVALID, 0u);
|
||||
return cast(ReturnType!decode_code_point)DecodeError();
|
||||
}
|
||||
code_point_length = following_bytes + 1u;
|
||||
for (size_t i = 0u; i < following_bytes; i++)
|
||||
@@ -104,12 +139,12 @@ class <%= @classname %>
|
||||
char b = input[i + 1u];
|
||||
if ((b & 0xC0u) != 0x80u)
|
||||
{
|
||||
return DecodedCodePoint(CODE_POINT_INVALID, 0u);
|
||||
return cast(ReturnType!decode_code_point)DecodeError();
|
||||
}
|
||||
code_point = (code_point << 6u) | (b & 0x3Fu);
|
||||
}
|
||||
}
|
||||
return DecodedCodePoint(code_point, code_point_length);
|
||||
return cast(ReturnType!decode_code_point)Success(code_point, code_point_length);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,17 +152,52 @@ class <%= @classname %>
|
||||
{
|
||||
private struct Transition
|
||||
{
|
||||
uint first;
|
||||
uint last;
|
||||
CodePoint first;
|
||||
CodePoint last;
|
||||
uint destination;
|
||||
}
|
||||
|
||||
private struct UserCodeID
|
||||
{
|
||||
enum count = <%= (@grammar.patterns.map(&:code_id).compact.max || -1) + 1%>;
|
||||
uint user_code_id;
|
||||
alias user_code_id this;
|
||||
|
||||
@disable this();
|
||||
|
||||
this(uint user_code_id)
|
||||
{
|
||||
this.user_code_id = user_code_id;
|
||||
}
|
||||
|
||||
static UserCodeID invalid()
|
||||
{
|
||||
return UserCodeID(count);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return user_code_id < count;
|
||||
}
|
||||
|
||||
bool is_invalid() const
|
||||
{
|
||||
return !is_valid();
|
||||
}
|
||||
}
|
||||
|
||||
private struct State
|
||||
{
|
||||
uint transition_table_index;
|
||||
uint n_transitions;
|
||||
uint token;
|
||||
uint code_id;
|
||||
Token token;
|
||||
UserCodeID code_id;
|
||||
bool drop;
|
||||
|
||||
bool accepts() const
|
||||
{
|
||||
return drop || token.is_valid() || code_id.is_valid();
|
||||
}
|
||||
}
|
||||
|
||||
private struct Mode
|
||||
@@ -136,29 +206,47 @@ class <%= @classname %>
|
||||
}
|
||||
|
||||
<% transition_table, state_table, mode_table = @lexer.build_tables %>
|
||||
private static immutable Transition transitions[] = [
|
||||
private static immutable Transition[] transitions = [
|
||||
<% transition_table.each do |transition_table_entry| %>
|
||||
Transition(<%= transition_table_entry[:first] %>u, <%= transition_table_entry[:last] %>u, <%= transition_table_entry[:destination] %>u),
|
||||
<% end %>
|
||||
];
|
||||
|
||||
private static immutable State states[] = [
|
||||
private static immutable State[] states = [
|
||||
<% state_table.each do |state_table_entry| %>
|
||||
State(<%= state_table_entry[:transition_table_index] %>u,
|
||||
<%= state_table_entry[:n_transitions] %>u,
|
||||
<%= state_table_entry[:token] %>u,
|
||||
<%= state_table_entry[:code_id] %>u),
|
||||
<% if state_table_entry[:token] %>
|
||||
Token(<%= state_table_entry[:token] %>u),
|
||||
<% else %>
|
||||
Token.invalid(),
|
||||
<% end %>
|
||||
<% if state_table_entry[:code_id] %>
|
||||
UserCodeID(<%= state_table_entry[:code_id] %>u),
|
||||
<% else %>
|
||||
UserCodeID.invalid(),
|
||||
<% end %>
|
||||
<%= state_table_entry[:drop] %>),
|
||||
<% end %>
|
||||
];
|
||||
|
||||
private static immutable Mode modes[] = [
|
||||
private static immutable Mode[] modes = [
|
||||
<% mode_table.each do |mode_table_entry| %>
|
||||
Mode(<%= mode_table_entry[:state_table_offset] %>),
|
||||
<% end %>
|
||||
];
|
||||
|
||||
struct LexedToken
|
||||
struct Result
|
||||
{
|
||||
enum Type
|
||||
{
|
||||
DECODE_ERROR,
|
||||
DROP,
|
||||
TOKEN,
|
||||
UNEXPECTED_INPUT,
|
||||
}
|
||||
|
||||
Type type;
|
||||
size_t row;
|
||||
size_t col;
|
||||
size_t length;
|
||||
@@ -178,14 +266,14 @@ class <%= @classname %>
|
||||
m_mode = <%= @lexer.mode_id("default") %>;
|
||||
}
|
||||
|
||||
LexedToken lex_token()
|
||||
Result lex_token()
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
LexedToken lt = attempt_lex_token();
|
||||
if (lt.token < _TOKEN_COUNT)
|
||||
Result result = attempt_lex_token();
|
||||
if (result.token < _TOKEN_COUNT)
|
||||
{
|
||||
return lt;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -195,12 +283,12 @@ class <%= @classname %>
|
||||
*
|
||||
* @param code_id The ID of the user code block to execute.
|
||||
* @param match Matched text for this pattern.
|
||||
* @param lt LexedToken lexer result in progress.
|
||||
* @param result Result lexer result in progress.
|
||||
*
|
||||
* @return Token ID to accept, or _TOKEN_COUNT if the user code does
|
||||
* @return Token to accept, or invalid token if the user code does
|
||||
* not explicitly return a token.
|
||||
*/
|
||||
private uint user_code(uint code_id, string match, LexedToken * lt)
|
||||
private Token user_code(UserCodeID code_id, string match, Result * result)
|
||||
{
|
||||
switch (code_id)
|
||||
{
|
||||
@@ -214,107 +302,259 @@ class <%= @classname %>
|
||||
default: break;
|
||||
}
|
||||
|
||||
return _TOKEN_COUNT;
|
||||
return Token.invalid();
|
||||
}
|
||||
|
||||
private LexedToken attempt_lex_token()
|
||||
private Result attempt_lex_token()
|
||||
{
|
||||
LexedToken lt;
|
||||
lt.row = m_input_row;
|
||||
lt.col = m_input_col;
|
||||
lt.token = _TOKEN_COUNT;
|
||||
struct MatchInfo
|
||||
Result result;
|
||||
result.row = m_input_row;
|
||||
result.col = m_input_col;
|
||||
result.token = _TOKEN_COUNT;
|
||||
auto match_result = find_longest_match();
|
||||
if (match_result.is_eof())
|
||||
{
|
||||
size_t length;
|
||||
size_t delta_row;
|
||||
size_t delta_col;
|
||||
uint token;
|
||||
uint code_id;
|
||||
result.type = Result.Type.TOKEN;
|
||||
result.token = TOKEN___EOF;
|
||||
return result;
|
||||
}
|
||||
MatchInfo longest_match_info;
|
||||
longest_match_info.token = _TOKEN_COUNT;
|
||||
MatchInfo attempt_match_info;
|
||||
else if (match_result.is_decode_error())
|
||||
{
|
||||
result.type = Result.Type.DECODE_ERROR;
|
||||
return result;
|
||||
}
|
||||
else if (match_result.is_unexpected_input())
|
||||
{
|
||||
result.type = Result.Type.UNEXPECTED_INPUT;
|
||||
return result;
|
||||
}
|
||||
uint token_to_accept = match_result.accepting_state.token;
|
||||
if (match_result.accepting_state.code_id.is_valid())
|
||||
{
|
||||
Token user_code_token = user_code(match_result.accepting_state.code_id, m_input[m_input_position..(m_input_position + match_result.length)], &result);
|
||||
/* An invalid Token from user_code() means that the user
|
||||
* code did not explicitly return a token. So only override
|
||||
* the token to return if the user code does explicitly
|
||||
* return a token. */
|
||||
if (user_code_token.is_valid())
|
||||
{
|
||||
token_to_accept = user_code_token.token;
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the input position tracking. */
|
||||
m_input_position += match_result.length;
|
||||
m_input_row += match_result.delta_row;
|
||||
if (match_result.delta_row != 0u)
|
||||
{
|
||||
m_input_col = match_result.delta_col;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input_col += match_result.delta_col;
|
||||
}
|
||||
result.token = token_to_accept;
|
||||
result.length = match_result.length;
|
||||
if (match_result.accepting_state.drop)
|
||||
{
|
||||
result.type = Result.Type.DROP;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.type = Result.Type.TOKEN;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result type for find_longest_match().
|
||||
*
|
||||
* Alternatives:
|
||||
* - decode_error
|
||||
* - eof
|
||||
* - found_match(length, delta_row, delta_col, accepting_state)
|
||||
* - unexpected_input(unexpected_input_length)
|
||||
*/
|
||||
struct FindLongestMatchResult
|
||||
{
|
||||
enum : ubyte
|
||||
{
|
||||
FOUND_MATCH,
|
||||
DECODE_ERROR,
|
||||
EOF,
|
||||
UNEXPECTED_INPUT,
|
||||
}
|
||||
|
||||
ubyte type;
|
||||
alias type this;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
size_t length;
|
||||
size_t delta_row;
|
||||
size_t delta_col;
|
||||
const(State) * accepting_state;
|
||||
}
|
||||
size_t unexpected_input_length;
|
||||
}
|
||||
|
||||
this(ubyte type)
|
||||
{
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
this(ubyte type, size_t unexpected_input_length)
|
||||
{
|
||||
this.type = type;
|
||||
this.unexpected_input_length = unexpected_input_length;
|
||||
}
|
||||
|
||||
this(ubyte type, size_t length, size_t delta_row, size_t delta_col, const(State) * accepting_state)
|
||||
{
|
||||
this.type = type;
|
||||
this.length = length;
|
||||
this.delta_row = delta_row;
|
||||
this.delta_col = delta_col;
|
||||
this.accepting_state = accepting_state;
|
||||
}
|
||||
|
||||
static FindLongestMatchResult found_match(size_t length, size_t delta_row, size_t delta_col, const(State) * accepting_state)
|
||||
{
|
||||
return FindLongestMatchResult(FOUND_MATCH, length, delta_row, delta_col, accepting_state);
|
||||
}
|
||||
|
||||
static FindLongestMatchResult decode_error()
|
||||
{
|
||||
return FindLongestMatchResult(DECODE_ERROR);
|
||||
}
|
||||
|
||||
static FindLongestMatchResult eof()
|
||||
{
|
||||
return FindLongestMatchResult(EOF);
|
||||
}
|
||||
|
||||
static FindLongestMatchResult unexpected_input(size_t unexpected_input_length)
|
||||
{
|
||||
return FindLongestMatchResult(UNEXPECTED_INPUT, unexpected_input_length);
|
||||
}
|
||||
|
||||
bool is_found_match()
|
||||
{
|
||||
return type == FOUND_MATCH;
|
||||
}
|
||||
|
||||
bool is_decode_error()
|
||||
{
|
||||
return type == DECODE_ERROR;
|
||||
}
|
||||
|
||||
bool is_eof()
|
||||
{
|
||||
return type == EOF;
|
||||
}
|
||||
|
||||
bool is_unexpected_input()
|
||||
{
|
||||
return type == UNEXPECTED_INPUT;
|
||||
}
|
||||
}
|
||||
|
||||
private FindLongestMatchResult find_longest_match()
|
||||
{
|
||||
FindLongestMatchResult longest_match = FindLongestMatchResult.found_match(0, 0, 0, null);
|
||||
FindLongestMatchResult attempt_match = longest_match;
|
||||
uint current_state = modes[m_mode].state_table_offset;
|
||||
for (;;)
|
||||
{
|
||||
auto decoded = Decoder.decode_code_point(m_input[(m_input_position + attempt_match_info.length)..(m_input.length)]);
|
||||
if (decoded.code_point == Decoder.CODE_POINT_INVALID)
|
||||
{
|
||||
lt.token = _TOKEN_DECODE_ERROR;
|
||||
return lt;
|
||||
}
|
||||
bool lex_continue = false;
|
||||
if (decoded.code_point != Decoder.CODE_POINT_EOF)
|
||||
{
|
||||
uint dest = transition(current_state, decoded.code_point);
|
||||
if (dest != cast(uint)-1)
|
||||
{
|
||||
lex_continue = true;
|
||||
attempt_match_info.length += decoded.code_point_length;
|
||||
if (decoded.code_point == '\n')
|
||||
{
|
||||
attempt_match_info.delta_row++;
|
||||
attempt_match_info.delta_col = 0u;
|
||||
}
|
||||
else
|
||||
{
|
||||
attempt_match_info.delta_col++;
|
||||
}
|
||||
current_state = dest;
|
||||
if ((states[current_state].token != _TOKEN_COUNT) ||
|
||||
(states[current_state].code_id != 0xFFFF_FFFFu))
|
||||
{
|
||||
attempt_match_info.token = states[current_state].token;
|
||||
attempt_match_info.code_id = states[current_state].code_id;
|
||||
longest_match_info = attempt_match_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (attempt_match_info.length == 0u)
|
||||
{
|
||||
lt.token = TOKEN_0EOF;
|
||||
break;
|
||||
}
|
||||
if (!lex_continue)
|
||||
{
|
||||
bool pattern_accepted = false;
|
||||
uint token_to_accept = longest_match_info.token;
|
||||
if (longest_match_info.code_id != 0xFFFF_FFFFu)
|
||||
{
|
||||
uint user_code_token = user_code(longest_match_info.code_id, m_input[m_input_position..(m_input_position + longest_match_info.length)], <);
|
||||
/* A return of _TOKEN_COUNT from user_code() means
|
||||
* that the user code did not explicitly return a
|
||||
* token. So only override the token to return if the
|
||||
* user code does explicitly return a token. */
|
||||
if (user_code_token != _TOKEN_COUNT)
|
||||
{
|
||||
token_to_accept = user_code_token;
|
||||
}
|
||||
pattern_accepted = true;
|
||||
}
|
||||
if (pattern_accepted || (token_to_accept != _TOKEN_COUNT))
|
||||
{
|
||||
/* Update the input position tracking. */
|
||||
m_input_position += longest_match_info.length;
|
||||
m_input_row += longest_match_info.delta_row;
|
||||
if (longest_match_info.delta_row != 0u)
|
||||
{
|
||||
m_input_col = longest_match_info.delta_col;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_input_col += longest_match_info.delta_col;
|
||||
}
|
||||
lt.token = token_to_accept;
|
||||
lt.length = longest_match_info.length;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Decoder.decode_code_point(m_input[(m_input_position + attempt_match.length)..(m_input.length)]).match!(
|
||||
(Decoder.DecodeError de) {return FindLongestMatchResult.decode_error();},
|
||||
(Decoder.EOF e) {
|
||||
/* We hit EOF. */
|
||||
if (longest_match.length > 0)
|
||||
{
|
||||
/* We have a match, so use it. */
|
||||
return longest_match;
|
||||
}
|
||||
else if (attempt_match.length != 0)
|
||||
{
|
||||
/* There is a partial match - error! */
|
||||
return FindLongestMatchResult.unexpected_input(attempt_match.length);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Valid EOF return. */
|
||||
return FindLongestMatchResult.eof();
|
||||
}
|
||||
},
|
||||
(Decoder.Success decoded) {
|
||||
auto transition_result = transition(current_state, decoded.code_point);
|
||||
if (transition_result.found())
|
||||
{
|
||||
lex_continue = true;
|
||||
attempt_match.length += decoded.code_point_length;
|
||||
if (decoded.code_point == '\n')
|
||||
{
|
||||
attempt_match.delta_row++;
|
||||
attempt_match.delta_col = 0u;
|
||||
}
|
||||
else
|
||||
{
|
||||
attempt_match.delta_col++;
|
||||
}
|
||||
current_state = transition_result.destination();
|
||||
if (states[current_state].accepts())
|
||||
{
|
||||
attempt_match.accepting_state = &states[current_state];
|
||||
longest_match = attempt_match;
|
||||
}
|
||||
}
|
||||
else if (longest_match.length > 0)
|
||||
{
|
||||
return longest_match;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FindLongestMatchResult.unexpected_input(attempt_match.length + decoded.code_point_length);
|
||||
}
|
||||
});
|
||||
}
|
||||
return lt;
|
||||
}
|
||||
|
||||
private uint transition(uint current_state, uint code_point)
|
||||
/**
|
||||
* Result type for transition().
|
||||
*
|
||||
* Alternatives:
|
||||
* - found(destination)
|
||||
* - not_found
|
||||
*/
|
||||
struct TransitionResult
|
||||
{
|
||||
private uint m_destination;
|
||||
|
||||
static TransitionResult found(uint destination)
|
||||
{
|
||||
return TransitionResult(destination);
|
||||
}
|
||||
|
||||
static TransitionResult not_found()
|
||||
{
|
||||
return TransitionResult(cast(uint)-1);
|
||||
}
|
||||
|
||||
bool found()
|
||||
{
|
||||
return m_destination != cast(uint)-1;
|
||||
}
|
||||
|
||||
@property uint destination()
|
||||
{
|
||||
return m_destination;
|
||||
}
|
||||
}
|
||||
|
||||
private TransitionResult transition(uint current_state, uint code_point)
|
||||
{
|
||||
uint transition_table_index = states[current_state].transition_table_index;
|
||||
for (uint i = 0u; i < states[current_state].n_transitions; i++)
|
||||
@@ -322,10 +562,11 @@ class <%= @classname %>
|
||||
if ((transitions[transition_table_index + i].first <= code_point) &&
|
||||
(code_point <= transitions[transition_table_index + i].last))
|
||||
{
|
||||
return transitions[transition_table_index + i].destination;
|
||||
uint destination = transitions[transition_table_index + i].destination;
|
||||
return TransitionResult.found(destination);
|
||||
}
|
||||
}
|
||||
return cast(uint)-1;
|
||||
return TransitionResult.not_found();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,19 +606,19 @@ class <%= @classname %>
|
||||
}
|
||||
|
||||
<% state_table, shift_table, reduce_table = @parser.build_tables %>
|
||||
private static immutable Shift shifts[] = [
|
||||
private static immutable Shift[] shifts = [
|
||||
<% shift_table.each do |shift| %>
|
||||
Shift(<%= shift[:token_id] %>u, <%= shift[:state_id] %>u),
|
||||
<% end %>
|
||||
];
|
||||
|
||||
private static immutable Reduce reduces[] = [
|
||||
private static immutable Reduce[] reduces = [
|
||||
<% reduce_table.each do |reduce| %>
|
||||
Reduce(<%= reduce[:token_id] %>u, <%= reduce[:rule_id] %>u, <%= reduce[:rule_set_id] %>u, <%= reduce[:n_states] %>u),
|
||||
<% end %>
|
||||
];
|
||||
|
||||
private static immutable State states[] = [
|
||||
private static immutable State[] states = [
|
||||
<% state_table.each do |state| %>
|
||||
State(<%= state[:shift_index] %>u, <%= state[:n_shifts] %>u, <%= state[:reduce_index] %>u, <%= state[:n_reduces] %>u),
|
||||
<% end %>
|
||||
@@ -394,7 +635,7 @@ class <%= @classname %>
|
||||
|
||||
bool parse()
|
||||
{
|
||||
Lexer.LexedToken lexed_token;
|
||||
Lexer.Result lexed_token;
|
||||
uint token = _TOKEN_COUNT;
|
||||
StateValue[] statevalues = new StateValue[](1);
|
||||
uint reduced_rule_set = 0xFFFFFFFFu;
|
||||
@@ -414,7 +655,7 @@ class <%= @classname %>
|
||||
if (shift_state == 0xFFFFFFFFu)
|
||||
{
|
||||
shift_state = check_shift(statevalues[$-1].state, token);
|
||||
if ((shift_state != 0xFFFFFFFFu) && (token == TOKEN_0EOF))
|
||||
if ((shift_state != 0xFFFFFFFFu) && (token == TOKEN___EOF))
|
||||
{
|
||||
/* Successful parse. */
|
||||
parse_result = statevalues[$-1].pvalue;
|
||||
|
||||
@@ -25,12 +25,6 @@ require_relative "propane/version"
|
||||
|
||||
class Propane
|
||||
|
||||
# Decoding error.
|
||||
TOKEN_DECODE_ERROR = 0xFFFFFFFD
|
||||
|
||||
# Token ID for a "dropped" token.
|
||||
TOKEN_DROP = 0xFFFFFFFE
|
||||
|
||||
class Error < RuntimeError
|
||||
end
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ class Propane
|
||||
# Expanded user code block.
|
||||
def expand_code(code, parser, rule, pattern)
|
||||
code = code.gsub(/\$token\(([$\w]+)\)/) do |match|
|
||||
"TOKEN_#{Token.code_name($1)}"
|
||||
"Token(TOKEN_#{Token.code_name($1)})"
|
||||
end
|
||||
if parser
|
||||
code = code.gsub(/\$\$/) do |match|
|
||||
@@ -195,7 +195,7 @@ class Propane
|
||||
end
|
||||
else
|
||||
code = code.gsub(/\$\$/) do |match|
|
||||
"lt.pvalue.v_#{pattern.ptypename}"
|
||||
"result.pvalue.v_#{pattern.ptypename}"
|
||||
end
|
||||
code = code.gsub(/\$mode\(([a-zA-Z_][a-zA-Z_0-9]*)\)/) do |match|
|
||||
mode_name = $1
|
||||
|
||||
@@ -2,7 +2,8 @@ class Propane
|
||||
|
||||
class Grammar
|
||||
|
||||
IDENTIFIER_REGEX = /[a-zA-Z_][a-zA-Z_0-9]*/
|
||||
# Reserve identifiers beginning with a double-underscore for internal use.
|
||||
IDENTIFIER_REGEX = /(?:[a-zA-Z]|_[a-zA-Z0-9])[a-zA-Z_0-9]*/
|
||||
|
||||
attr_reader :classname
|
||||
attr_reader :modulename
|
||||
|
||||
@@ -24,25 +24,19 @@ class Propane
|
||||
}
|
||||
states = mode_info[:dfa].enumerate
|
||||
states.each do |state, id|
|
||||
drop = state.accepts && state.accepts.drop?
|
||||
token =
|
||||
if state.accepts.nil?
|
||||
@grammar.tokens.size
|
||||
elsif state.accepts.drop?
|
||||
TOKEN_DROP
|
||||
elsif state.accepts.token
|
||||
if state.accepts && state.accepts.token
|
||||
state.accepts.token.id
|
||||
else
|
||||
@grammar.tokens.size
|
||||
end
|
||||
code_id =
|
||||
if state.accepts && state.accepts.code_id
|
||||
state.accepts.code_id
|
||||
else
|
||||
0xFFFF_FFFF
|
||||
end
|
||||
state_table << {
|
||||
transition_table_index: transition_table.size,
|
||||
n_transitions: state.transitions.size,
|
||||
drop: drop,
|
||||
token: token,
|
||||
code_id: code_id,
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class Propane
|
||||
# @return [String]
|
||||
# Name of the token to use in code (special characters replaced).
|
||||
def code_name(name)
|
||||
name.sub(/^\$/, "0")
|
||||
name.sub(/^\$/, "__")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ describe Propane do
|
||||
end
|
||||
|
||||
def compile(*test_files)
|
||||
result = system(*%w[gdc -funittest -o spec/run/testparser spec/run/testparser.d -Ispec], *test_files)
|
||||
result = system(*%w[ldc2 --unittest -of spec/run/testparser spec/run/testparser.d -Ispec], *test_files)
|
||||
expect(result).to be_truthy
|
||||
end
|
||||
|
||||
@@ -28,6 +28,7 @@ describe Propane do
|
||||
stdout, stderr, status = Open3.capture3("spec/run/testparser")
|
||||
File.binwrite("spec/run/.stderr", stderr)
|
||||
File.binwrite("spec/run/.stdout", stdout)
|
||||
stderr.sub!(/^.*modules passed unittests\n/, "")
|
||||
Results.new(stdout, stderr, status)
|
||||
end
|
||||
|
||||
|
||||
+26
-26
@@ -8,45 +8,45 @@ int main()
|
||||
|
||||
unittest
|
||||
{
|
||||
alias DCP = Testparser.Decoder.DecodedCodePoint;
|
||||
DCP dcp;
|
||||
alias Result = Testparser.Decoder.Result;
|
||||
Result result;
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("5");
|
||||
assert(dcp == DCP('5', 1u));
|
||||
result = Testparser.Decoder.decode_code_point("5");
|
||||
assert(result == Result.success('5', 1u));
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("");
|
||||
assert(dcp == DCP(Testparser.Decoder.CODE_POINT_EOF, 0u));
|
||||
result = Testparser.Decoder.decode_code_point("");
|
||||
assert(result == Result.eof());
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("\xC2\xA9");
|
||||
assert(dcp == DCP(0xA9u, 2u));
|
||||
result = Testparser.Decoder.decode_code_point("\xC2\xA9");
|
||||
assert(result == Result.success(0xA9u, 2u));
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("\xf0\x9f\xa7\xa1");
|
||||
assert(dcp == DCP(0x1F9E1, 4u));
|
||||
result = Testparser.Decoder.decode_code_point("\xf0\x9f\xa7\xa1");
|
||||
assert(result == Result.success(0x1F9E1, 4u));
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("\xf0\x9f\x27");
|
||||
assert(dcp == DCP(Testparser.Decoder.CODE_POINT_INVALID, 0u));
|
||||
result = Testparser.Decoder.decode_code_point("\xf0\x9f\x27");
|
||||
assert(result == Result.decode_error());
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("\xf0\x9f\xa7\xFF");
|
||||
assert(dcp == DCP(Testparser.Decoder.CODE_POINT_INVALID, 0u));
|
||||
result = Testparser.Decoder.decode_code_point("\xf0\x9f\xa7\xFF");
|
||||
assert(result == Result.decode_error());
|
||||
|
||||
dcp = Testparser.Decoder.decode_code_point("\xfe");
|
||||
assert(dcp == DCP(Testparser.Decoder.CODE_POINT_INVALID, 0u));
|
||||
result = Testparser.Decoder.decode_code_point("\xfe");
|
||||
assert(result == Result.decode_error());
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
alias LT = Testparser.Lexer.LexedToken;
|
||||
alias Result = Testparser.Lexer.Result;
|
||||
string input = "5 + 4 * \n677 + 567";
|
||||
Testparser.Lexer lexer = new Testparser.Lexer(input);
|
||||
assert(lexer.lex_token() == LT(0, 0, 1, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == LT(0, 2, 1, Testparser.TOKEN_plus));
|
||||
assert(lexer.lex_token() == LT(0, 4, 1, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == LT(0, 6, 1, Testparser.TOKEN_times));
|
||||
assert(lexer.lex_token() == LT(1, 0, 3, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == LT(1, 4, 1, Testparser.TOKEN_plus));
|
||||
assert(lexer.lex_token() == LT(1, 6, 3, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == LT(1, 9, 0, Testparser.TOKEN_0EOF));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 0, 0, 1, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 0, 2, 1, Testparser.TOKEN_plus));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 0, 4, 1, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 0, 6, 1, Testparser.TOKEN_times));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 1, 0, 3, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 1, 4, 1, Testparser.TOKEN_plus));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 1, 6, 3, Testparser.TOKEN_int));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 1, 9, 0, Testparser.TOKEN___EOF));
|
||||
|
||||
lexer = new Testparser.Lexer("");
|
||||
assert(lexer.lex_token() == LT(0, 0, 0, Testparser.TOKEN_0EOF));
|
||||
assert(lexer.lex_token() == Result(Result.Type.TOKEN, 0, 0, 0, Testparser.TOKEN___EOF));
|
||||
}
|
||||
Reference in new issue
Block a user