Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
170 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
require "../pegasus/language_def.cr" | ||
require "../pegasus/json.cr" | ||
require "option_parser" | ||
require "ecr" | ||
|
||
module Pegasus | ||
module Language | ||
class LanguageData | ||
def output(io, prefix) | ||
ECR.embed "src/crystal/pegasus_crystal_template.ecr", io | ||
end | ||
end | ||
end | ||
end | ||
|
||
prefix = "Pegasus::Generated" | ||
file_name = "parser" | ||
stdout = false | ||
|
||
OptionParser.parse! do |parser| | ||
parser.banner = "Usage: pegasus-crystal [arguments]" | ||
parser.on("-S", "--standard-out", "Combines the header and implementation files, and prints to standard out") { stdout = true } | ||
parser.on("-P PREFIX", "--prefix PREFIX", "Specify the prefix for generated code") do |p| | ||
prefix = p | ||
end | ||
parser.on("-f FILE", "--file-name=FILE", "Sets output file name") { |file| file_name = file } | ||
parser.on("-h", "--help", "Displays this message") { puts parser } | ||
end | ||
|
||
data = Pegasus::Language::LanguageData.from_json STDIN | ||
if stdout | ||
data.output STDOUT, prefix | ||
else | ||
file = File.open(file_name + ".cr", mode = "w") | ||
data.output file, prefix | ||
file.close | ||
end | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
module Pegasus::Generated | ||
MAX_TERMINAL = <%= @max_terminal %> | ||
LEX_FINAL_TABLE = [ <% @lex_final_table.each do |final| %> <%= final %>_i64, <% end %> ] | ||
LEX_STATE_TABLE = [ <% @lex_state_table.each do |state| %> | ||
[ <% state.each do |transition| %> <%= transition %>_i64, <% end %> ],<%- end %> | ||
] | ||
PARSE_ACTION_TABLE = [ <% @parse_action_table.each do |state| %> | ||
[ <% state.each do |transition| %> <%= transition %>_i64, <% end %> ],<%- end %> | ||
] | ||
PARSE_STATE_TABLE = [ <% @parse_state_table.each do |state| %> | ||
[ <% state.each do |transition| %> <%= transition %>_i64, <% end %> ],<%- end %> | ||
] | ||
ITEMS = <% if @items.size == 0 %> [] of Tuple(Int64, Int64) <% else %> [ <% @items.each do |item| %> | ||
{ <%= item.head.id %>_i64, <%= item.body.size %>_i64 },<%- end %> | ||
] <%- end %> | ||
|
||
extend self | ||
|
||
abstract class Tree | ||
abstract def table_index | ||
end | ||
|
||
class NonterminalTree < Tree | ||
getter nonterminal_id : Int64 | ||
getter children : Array(Tree) | ||
|
||
def initialize(@nonterminal_id, @children = [] of Tree) | ||
end | ||
|
||
def table_index | ||
nonterminal_id + 1 + MAX_TERMINAL + 1 | ||
end | ||
|
||
def name | ||
case nonterminal_id<% @nonterminals.each do |nt| %> | ||
when <%= nt[1].id %>_i64 | ||
<%= nt[0].dump -%> | ||
<%- end %> | ||
else | ||
"???" | ||
end | ||
end | ||
end | ||
|
||
class TerminalTree < Tree | ||
getter terminal_id : Int64 | ||
getter string : String | ||
|
||
def initialize(@terminal_id, @string) | ||
end | ||
|
||
def table_index | ||
terminal_id + 1 | ||
end | ||
end | ||
|
||
class Token | ||
getter terminal_id : Int64 | ||
getter string : String | ||
|
||
def initialize(@terminal_id, @string) | ||
end | ||
end | ||
|
||
def lex(string) | ||
index = 0 | ||
tokens = [] of Token | ||
bytes = string.bytes | ||
|
||
while index < bytes.size | ||
start_index = index | ||
last_match_index = -1 | ||
last_pattern = -1_i64 | ||
state = 1 | ||
|
||
while index < bytes.size | ||
state = LEX_STATE_TABLE[state][bytes[index]] | ||
id = LEX_FINAL_TABLE[state] | ||
|
||
break if state == 0 | ||
next if id == 0 | ||
|
||
last_match_index = index | ||
last_pattern = id | ||
index += 1 | ||
end | ||
|
||
raise "Invalid character at position #{index}" if last_match_index == -1 | ||
tokens << Token.new(last_pattern - 1, string[start_index..last_match_index]) | ||
end | ||
|
||
return tokens | ||
end | ||
|
||
def parse(tokens) | ||
tree_stack = [ ] of Tree | ||
state_stack = [ 1_i64 ] | ||
index = 0 | ||
|
||
while true | ||
break if tree_stack.last?.try(&.as?(NonterminalTree)).try(&.nonterminal_id) == 0 | ||
token = tokens[index]? | ||
action = PARSE_ACTION_TABLE[state_stack.last][token.try(&.terminal_id.+(1)) || 0_i64] | ||
raise "Invalid token #{token.try &.string}" if action == -1 | ||
|
||
if action == 0 | ||
raise "Unexpected end of file" unless token | ||
tree_stack << TerminalTree.new token.terminal_id, token.string | ||
index += 1 | ||
else | ||
item = ITEMS[action - 1] | ||
tree = NonterminalTree.new item[0] | ||
|
||
item[1].times do | ||
tree.children.insert 0, tree_stack.pop | ||
state_stack.pop | ||
end | ||
|
||
tree_stack << tree | ||
end | ||
|
||
state_stack << PARSE_STATE_TABLE[state_stack.last][tree_stack.last.table_index] | ||
end | ||
return tree_stack.last | ||
end | ||
|
||
def process(string) | ||
parse(lex(string)) | ||
end | ||
end |