Allow defining multiple parser types and assigning parser types to tokens and rules
This commit is contained in:
parent
8dc27686aa
commit
e4a160f918
@ -38,7 +38,7 @@ class Propane
|
|||||||
raise Error.new("No patterns found for default mode")
|
raise Error.new("No patterns found for default mode")
|
||||||
end
|
end
|
||||||
# Add EOF token.
|
# Add EOF token.
|
||||||
@grammar.tokens << Token.new("$EOF", nil)
|
@grammar.tokens << Token.new("$EOF", nil, nil)
|
||||||
tokens_by_name = {}
|
tokens_by_name = {}
|
||||||
@grammar.tokens.each_with_index do |token, token_id|
|
@grammar.tokens.each_with_index do |token, token_id|
|
||||||
# Assign token ID.
|
# Assign token ID.
|
||||||
@ -54,7 +54,7 @@ class Propane
|
|||||||
raise Error.new("Start rule not found")
|
raise Error.new("Start rule not found")
|
||||||
end
|
end
|
||||||
# Add "real" start rule.
|
# Add "real" start rule.
|
||||||
@grammar.rules.unshift(Rule.new("$Start", ["Start", "$EOF"], nil, nil))
|
@grammar.rules.unshift(Rule.new("$Start", ["Start", "$EOF"], nil, nil, nil))
|
||||||
rule_sets = {}
|
rule_sets = {}
|
||||||
rule_set_id = @grammar.tokens.size
|
rule_set_id = @grammar.tokens.size
|
||||||
@grammar.rules.each_with_index do |rule, rule_id|
|
@grammar.rules.each_with_index do |rule, rule_id|
|
||||||
|
@ -2,13 +2,15 @@ class Propane
|
|||||||
|
|
||||||
class Grammar
|
class Grammar
|
||||||
|
|
||||||
|
IDENTIFIER_REGEX = /[a-zA-Z_][a-zA-Z_0-9]*/
|
||||||
|
|
||||||
attr_reader :classname
|
attr_reader :classname
|
||||||
attr_reader :modulename
|
attr_reader :modulename
|
||||||
attr_reader :patterns
|
attr_reader :patterns
|
||||||
attr_reader :rules
|
attr_reader :rules
|
||||||
attr_reader :tokens
|
attr_reader :tokens
|
||||||
attr_reader :code_blocks
|
attr_reader :code_blocks
|
||||||
attr_reader :ptype
|
attr_reader :ptypes
|
||||||
|
|
||||||
def initialize(input)
|
def initialize(input)
|
||||||
@patterns = []
|
@patterns = []
|
||||||
@ -19,10 +21,14 @@ class Propane
|
|||||||
@next_line_number = @line_number
|
@next_line_number = @line_number
|
||||||
@mode = nil
|
@mode = nil
|
||||||
@input = input.gsub("\r\n", "\n")
|
@input = input.gsub("\r\n", "\n")
|
||||||
@ptype = "void *"
|
@ptypes = {"default" => "void *"}
|
||||||
parse_grammar!
|
parse_grammar!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def ptype
|
||||||
|
@ptypes["default"]
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def parse_grammar!
|
def parse_grammar!
|
||||||
@ -53,7 +59,7 @@ class Propane
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_mode_label!
|
def parse_mode_label!
|
||||||
if md = consume!(/([a-zA-Z_][a-zA-Z_0-9]*)\s*:/)
|
if md = consume!(/(#{IDENTIFIER_REGEX})\s*:/)
|
||||||
@mode = md[1]
|
@mode = md[1]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -88,24 +94,28 @@ class Propane
|
|||||||
|
|
||||||
def parse_ptype_statement!
|
def parse_ptype_statement!
|
||||||
if consume!(/ptype\s+/)
|
if consume!(/ptype\s+/)
|
||||||
md = consume!(/([^;]+);/, "expected result type expression")
|
name = "default"
|
||||||
@ptype = md[1].strip
|
if md = consume!(/(#{IDENTIFIER_REGEX})\s*=\s*/)
|
||||||
|
name = md[1]
|
||||||
|
end
|
||||||
|
md = consume!(/([^;]+);/, "expected parser result type expression")
|
||||||
|
@ptypes[name] = md[1].strip
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_token_statement!
|
def parse_token_statement!
|
||||||
if consume!(/token\s+/)
|
if consume!(/token\s+/)
|
||||||
md = consume!(/([a-zA-Z_][a-zA-Z_0-9]*)/, "expected token name")
|
md = consume!(/(#{IDENTIFIER_REGEX})\s*/, "expected token name")
|
||||||
name = md[1]
|
name = md[1]
|
||||||
if consume!(/\s+/)
|
if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/)
|
||||||
pattern = parse_pattern!
|
ptypename = md[1]
|
||||||
end
|
end
|
||||||
pattern ||= name
|
pattern = parse_pattern! || name
|
||||||
consume!(/\s+/)
|
consume!(/\s+/)
|
||||||
unless code = parse_code_block!
|
unless code = parse_code_block!
|
||||||
consume!(/;/, "expected pattern or `;' or code block")
|
consume!(/;/, "expected pattern or `;' or code block")
|
||||||
end
|
end
|
||||||
token = Token.new(name, @line_number)
|
token = Token.new(name, ptypename, @line_number)
|
||||||
@tokens << token
|
@tokens << token
|
||||||
pattern = Pattern.new(pattern: pattern, token: token, line_number: @line_number, code: code, mode: @mode)
|
pattern = Pattern.new(pattern: pattern, token: token, line_number: @line_number, code: code, mode: @mode)
|
||||||
@patterns << pattern
|
@patterns << pattern
|
||||||
@ -115,12 +125,14 @@ class Propane
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_tokenid_statement!
|
def parse_tokenid_statement!
|
||||||
if md = consume!(/tokenid\s+(\S+?)\s*;/m)
|
if md = consume!(/tokenid\s+/)
|
||||||
|
md = consume!(/(#{IDENTIFIER_REGEX})\s*/, "expected token name")
|
||||||
name = md[1]
|
name = md[1]
|
||||||
unless name =~ /^[a-zA-Z_][a-zA-Z_0-9]*$/
|
if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/)
|
||||||
raise Error.new("Invalid token name #{name.inspect}")
|
ptypename = md[1]
|
||||||
end
|
end
|
||||||
token = Token.new(name, @line_number)
|
consume!(/;/, "expected `;'");
|
||||||
|
token = Token.new(name, ptypename, @line_number)
|
||||||
@tokens << token
|
@tokens << token
|
||||||
@mode = nil
|
@mode = nil
|
||||||
true
|
true
|
||||||
@ -142,13 +154,14 @@ class Propane
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_rule_statement!
|
def parse_rule_statement!
|
||||||
if md = consume!(/(\S+)\s*->\s*([^\n]*?)(?:;|<<\n(.*?)^>>\n)/m)
|
if md = consume!(/(#{IDENTIFIER_REGEX})\s*(?:\((#{IDENTIFIER_REGEX})\))?\s*->\s*/)
|
||||||
rule_name, components, code = *md[1, 3]
|
rule_name, ptypename = *md[1, 2]
|
||||||
unless rule_name =~ /^[a-zA-Z_][a-zA-Z_0-9]*$/
|
md = consume!(/((?:#{IDENTIFIER_REGEX}\s*)*)\s*/, "expected rule component list")
|
||||||
raise Error.new("Invalid rule name #{name.inspect}")
|
components = md[1].strip.split(/\s+/)
|
||||||
|
unless code = parse_code_block!
|
||||||
|
consume!(/;/, "expected pattern or `;' or code block")
|
||||||
end
|
end
|
||||||
components = components.strip.split(/\s+/)
|
@rules << Rule.new(rule_name, components, code, ptypename, @line_number)
|
||||||
@rules << Rule.new(rule_name, components, code, @line_number)
|
|
||||||
@mode = nil
|
@mode = nil
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
@ -14,6 +14,10 @@ class Propane
|
|||||||
# Rule ID.
|
# Rule ID.
|
||||||
attr_accessor :id
|
attr_accessor :id
|
||||||
|
|
||||||
|
# @return [String, nil]
|
||||||
|
# Parser type name.
|
||||||
|
attr_reader :ptypename
|
||||||
|
|
||||||
# @return [Integer]
|
# @return [Integer]
|
||||||
# Line number where the rule was defined in the input grammar.
|
# Line number where the rule was defined in the input grammar.
|
||||||
attr_reader :line_number
|
attr_reader :line_number
|
||||||
@ -34,12 +38,15 @@ class Propane
|
|||||||
# Rule components.
|
# Rule components.
|
||||||
# @param code [String]
|
# @param code [String]
|
||||||
# User code associated with the rule.
|
# User code associated with the rule.
|
||||||
|
# @param ptypename [String, nil]
|
||||||
|
# Parser type name.
|
||||||
# @param line_number [Integer]
|
# @param line_number [Integer]
|
||||||
# Line number where the rule was defined in the input grammar.
|
# Line number where the rule was defined in the input grammar.
|
||||||
def initialize(name, components, code, line_number)
|
def initialize(name, components, code, ptypename, line_number)
|
||||||
@name = name
|
@name = name
|
||||||
@components = components
|
@components = components
|
||||||
@code = code
|
@code = code
|
||||||
|
@ptypename = ptypename
|
||||||
@line_number = line_number
|
@line_number = line_number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,6 +18,10 @@ class Propane
|
|||||||
# Token name.
|
# Token name.
|
||||||
attr_reader :name
|
attr_reader :name
|
||||||
|
|
||||||
|
# @return [String, nil]
|
||||||
|
# Parser value type name.
|
||||||
|
attr_reader :ptypename
|
||||||
|
|
||||||
# @return [Integer, nil]
|
# @return [Integer, nil]
|
||||||
# Token ID.
|
# Token ID.
|
||||||
attr_accessor :id
|
attr_accessor :id
|
||||||
@ -28,14 +32,15 @@ class Propane
|
|||||||
|
|
||||||
# Construct a Token.
|
# Construct a Token.
|
||||||
#
|
#
|
||||||
# @param options [Hash]
|
# @param name [String, nil]
|
||||||
# Optional parameters.
|
|
||||||
# @option options [String, nil] :name
|
|
||||||
# Token name.
|
# Token name.
|
||||||
# @option options [Integer, nil] :line_number
|
# @param ptypename [String, nil]
|
||||||
|
# Parser value type for this token.
|
||||||
|
# @param line_number [Integer, nil]
|
||||||
# Line number where the token was defined in the input grammar.
|
# Line number where the token was defined in the input grammar.
|
||||||
def initialize(name, line_number)
|
def initialize(name, ptypename, line_number)
|
||||||
@name = name
|
@name = name
|
||||||
|
@ptypename = ptypename
|
||||||
@line_number = line_number
|
@line_number = line_number
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ EOF
|
|||||||
expect(grammar.classname).to eq "Foobar"
|
expect(grammar.classname).to eq "Foobar"
|
||||||
expect(grammar.modulename).to eq "a.b"
|
expect(grammar.modulename).to eq "a.b"
|
||||||
expect(grammar.ptype).to eq "XYZ *"
|
expect(grammar.ptype).to eq "XYZ *"
|
||||||
|
expect(grammar.ptypes).to eq("default" => "XYZ *")
|
||||||
|
|
||||||
o = grammar.tokens.find {|token| token.name == "while"}
|
o = grammar.tokens.find {|token| token.name == "while"}
|
||||||
expect(o).to_not be_nil
|
expect(o).to_not be_nil
|
||||||
@ -173,5 +174,42 @@ EOF
|
|||||||
expect(o).to_not be_nil
|
expect(o).to_not be_nil
|
||||||
expect(o.mode).to eq "m3"
|
expect(o.mode).to eq "m3"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "allows assigning ptypes to tokens and rules" do
|
||||||
|
input = <<EOF
|
||||||
|
ptype Subnode *;
|
||||||
|
ptype string = char *;
|
||||||
|
ptype integer = int;
|
||||||
|
ptype node = Node *;
|
||||||
|
|
||||||
|
token abc(string);
|
||||||
|
token bar;
|
||||||
|
tokenid int(integer);
|
||||||
|
|
||||||
|
Start (node) -> R;
|
||||||
|
R -> abc int;
|
||||||
|
EOF
|
||||||
|
grammar = Grammar.new(input)
|
||||||
|
|
||||||
|
o = grammar.tokens.find {|token| token.name == "abc"}
|
||||||
|
expect(o).to_not be_nil
|
||||||
|
expect(o.ptypename).to eq "string"
|
||||||
|
|
||||||
|
o = grammar.tokens.find {|token| token.name == "bar"}
|
||||||
|
expect(o).to_not be_nil
|
||||||
|
expect(o.ptypename).to be_nil
|
||||||
|
|
||||||
|
o = grammar.tokens.find {|token| token.name == "int"}
|
||||||
|
expect(o).to_not be_nil
|
||||||
|
expect(o.ptypename).to eq "integer"
|
||||||
|
|
||||||
|
o = grammar.rules.find {|rule| rule.name == "Start"}
|
||||||
|
expect(o).to_not be_nil
|
||||||
|
expect(o.ptypename).to eq "node"
|
||||||
|
|
||||||
|
o = grammar.rules.find {|rule| rule.name == "R"}
|
||||||
|
expect(o).to_not be_nil
|
||||||
|
expect(o.ptypename).to be_nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user