Skip to content

Instantly share code, notes, and snippets.

@frsyuki
Created March 26, 2011 20:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save frsyuki/888627 to your computer and use it in GitHub Desktop.
Save frsyuki/888627 to your computer and use it in GitHub Desktop.
MessagePack IDL言語仕様案とパーサの実装
// example1.msgspec
namespace com.example
namespace cpp example
namespace ruby Example
message BasicTypeExample {
1: int8 f1
2: int16 f2
3: int32 f3
4: int64 f4
5: uint8 f5
6: uint16 f6
7: uint32 f7
8: uint64 f8
9: float f9
10: double f10
11: bool f11
12: raw f12
13: string f13
14: date f14
}
message ContainerTypeExample {
1: list<string> f1
2: map<string,string> f2
3: set<string> f3
}
message OptionalExample {
1: string f1 // required non-nullable
2: required string f2 // required non-nullable
3: optional string f3 // optional non-nullable
4: int32 f4 // required non-nullable
5: required int32 f5 // required non-nullable
6: optional int32 f6 // optional non-nullable
}
message NullableExample {
1: string? f1 // required nullable
2: required string? f2 // required nullable
3: optional string? f3 // optional nullable
4: int32? f4 // required nullable
5: required int32? f5 // required nullable
6: optional int32? f6 // optional nullable
}
message EmptyExample {
}
message DefaultExample {
1: int32 f1 = 1212
2: string f2 = "test"
//3: list<int16> f3 = [1,2,3]
//4: map<string,int16> f4 = {"a":1, "b":10}
}
const int32 CONST1 = 11211
const string CONST2 = "test"
//const list<int16> CONST3 = [1,2,3]
//const map<string,int16> CONST4 = {"a":1, "b":10}
message ConstExample {
1: int32 f1 = CONST1
2: string f2 = CONST2
//3: list<int16> f2 = CONST3
//4: map<string,int16> f4 = CONST4
}
message BuiltInConstExample {
1: int8 f1 = INT8_MAX
2: int16 f2 = INT16_MAX
3: int32 f3 = INT32_MAX
4: int64 f4 = INT64_MAX
5: bool f5 = true
6: bool f6 = false
}
enum EnumExample {
1: RED
2: GREEN
3: BLUE
}
typedef map<string,string> PropertyMap;
message TypedefExampel {
1: PropertyMap f1
}
typedef<V> map<string,V> GenericStringMap;
message<V> GenericExample {
1: V f1
2: list<V> f2
3: GenericStringMap<V> f3
}
typedef GenericExample<string> GenericExampleSpecific1
typedef GenericExample<bool> GenericExampleSpecific2
message TypeSpecExample {
1: list<string> f1
}
typespec cpp TypeSpecExample.f1 std::vector<std::string>
typespec cpp PropertyMap std::tr1::unordered_map<std::string,std::string>
typespec<V> cpp GenericStringMap<V> std::unordered_map<string,V>
exception BasicExceptionExample {
1: string message
2: int32 code
}
exception SuperExceptionExample {
1: string message
2: int32 code
/*
exception SubExceptionExample1 {
3: string key
4: raw value
}
exception SubExceptionExample2 {
3: int32 flags
exception SubSubExceptionExample {
4: string value
}
}
*/
}
exception SubExceptionExample3 < SuperExceptionExample {
3: raw key
}
service BasicServiceExample {
string func1(1: string key, 2: string value)
string func2()
string? func3(1: string key, 2: string value)
string? func4()
void func5(1: string key, 2: string value)
}
service OptionalServiceExample {
string func1(1: string key, 2: string value)
string func2(1: string key, 2: optional string value)
string func3(1: string? key, 2: optional string? value)
string? func4(1: string key, 2: string value)
}
service ExceptionServiceExample {
void func1() throws SubExceptionExample2, SubExceptionExample3
void func2() throws BasicExceptionExample
}
service SubServiceExample < BasicExceptionExample {
! string func1(1: string key, 2: string value) // override
- string func1(1: string key, 2: string value) // delete
+ string func6() // add
string func7() // add
}
// バージョニング案1(関数ごとのバージョン)
/*
service VersionServiceExample {
void func1(1: string key) // version 0
void func1:1(1: string key, 2: optional string? value)
uint32? func1:2(1: string key, 2: optional string? value)
void func2:0(1: string key)
string func3()
string func3:1(1: optional int32 flags)
// func1 // func1:2
// func1:0 // func1:0
// func1:1 // func1:1
// func1:2 // func1:2
// func1:3 // エラー (VersionMismatch)
// func2 // func2:0
// func2:0 // func2:0
// func2:1 // エラー (VersionMismatch)
// func2:2 // エラー (VersionMismatch)
// func2:3 // エラー (VersionMismatch)
// func3 // func3:1
// func3:0 // func3:0
// func3:1 // func3:1
// func3:2 // エラー (VersionMismatch)
// func3:3 // エラー (VersionMismatch)
}
*/
// バージョニング案2(関数セットごとのバージョン)
service VersionServiceExample {
void func1(1: string key)
void func2(1: string key)
1:
! void func1(1: string key, 2: optional string? value)
+ string func3()
2:
! uint32? func1(1: string key, 2: optional string? value)
- void func2(1: string key)
! string func3(1: optional int32 flags)
// func1 // func1:2
// func1:0 // func1:0
// func1:1 // func1:1
// func1:2 // func1:2
// func1:3 // エラー (VersionMismatch)
// func2 // func2:0
// func2:0 // func2:0
// func2:1 // func2:0
// func2:2 // エラー (VersionMismatch)
// func2:3 // エラー (VersionMismatch)
// func3 // func3:2
// func3:0 // エラー (NoMethodFound)
// func3:1 // func3:2
// func3:2 // func3:2
// func3:3 // エラー (VersionMismatch)
}
include memo2.msgspec
server MyApplication {
OptionalServiceExample scope1 default
// func1
// func2
// func1:scope1
// func2:scope1
ExceptionServiceExample scope2
// func1:scope2
// func2:scope2
VersionServiceExample scope3
// func1:scope3
// func2:scope3
// func3:scope3
// func1:scope3:1
// func2:scope3:1
// func3:scope3:1
// ...
}
// example1-impl.msgspec
message IncludeTest {
}
require 'parslet'
require 'parslet/convenience'
require 'msgspec_visitor'
require 'msgspec_parser'
require 'msgspec_processor'
require 'pp'
processor = MessageSpec::Processor.new
begin
processor.parse_file('memo.msgspec')
rescue MessageSpec::MessageSpecError => e
puts e
end
pp processor.ast
module MessageSpec
class ParsletParser < Parslet::Parser
class << self
def sequence(name, separator, element, min=0)
if min == 0
eval %[rule(:#{name.to_s.dump}) {
(#{element}.as(:sequence_x) >> (#{separator} >> #{element}.as(:sequence_xs)).repeat).maybe.as(:sequence)
}]
else
eval %[rule(:#{name.to_s.dump}) {
(#{element}.as(:sequence_x) >> (#{separator} >> #{element}.as(:sequence_xs)).repeat(#{min-1})).as(:sequence)
}]
end
end
def keyword(string, name="k_"+string)
rule(name.to_sym) {
space? >> str(string) >> boundary
}
end
def separator(char, name)
rule(name.to_sym) {
space? >> str(char)
}
end
end
root :expression
rule(:expression) {
space? >> document >> space?
}
rule(:document) {
(include_ | definition).repeat.as(:document)
}
rule(:include_) {
k_include >>
path.as(:include) >>
eol
}
rule(:definition) {
namespace |
message |
enum |
exception |
const |
typedef |
typespec |
service |
server
}
rule(:namespace) {
k_namespace >> (
lang_name.as(:namespace_lang) >> namespace_name.as(:namespace_name) |
namespace_name.as(:namespace_name)
) >> eol
}
rule(:message) {
k_message >>
type_param_decl.maybe.as(:type_param_decl) >>
class_name.as(:message_name) >>
lt_extend_class.maybe.as(:super_class) >>
k_lwing >>
field.repeat.as(:message_body) >>
k_rwing
}
rule(:exception) {
k_exception >>
type_param_decl.maybe.as(:type_param_decl) >>
class_name.as(:exception_name) >>
lt_extend_class.maybe.as(:super_class) >>
k_lwing >>
field.repeat.as(:exception_body) >> # TODO nested exception?
k_rwing
}
rule(:field) {
field_element >> eol
}
rule(:field_element) {
field_id.as(:field_id) >>
field_modifier.maybe.as(:field_modifier) >>
field_type.as(:field_type) >>
field_name.as(:field_name) >>
eq_default_value.maybe.as(:field_default)
}
rule(:field_id) {
# terminal
space? >>
(str('0') | (match('[1-9]') >> match('[0-9]').repeat)).as(:val_int) >>
str(':') >> str(':').absent?
}
rule(:field_modifier) {
k_optional | k_required
}
rule(:eq_default_value) {
k_equal >> literal
}
rule(:enum) {
k_enum >>
class_name.as(:enum_name) >>
k_lwing >>
enum_field.repeat.as(:enum_body) >>
k_rwing
}
rule(:enum_field) {
enum_field_element >> eol
}
rule(:enum_field_element) {
field_id.as(:enum_field_id) >> field_name.as(:enum_field_name)
}
rule(:typedef) {
k_typedef >>
type_param_decl.maybe.as(:type_param_decl) >>
generic_type.as(:typedef_type) >>
generic_type.as(:typedef_name) >>
eol
}
rule(:const) {
k_const >>
field_type.as(:const_type) >>
const_name.as(:const_name) >>
k_equal >> literal.as(:const_value) >>
eol
}
rule(:typespec) {
k_typespec >>
type_param_decl.maybe.as(:type_param_decl) >>
lang_name.as(:typespec_lang) >> (
generic_type.as(:typespec_class) >> str('.') >> field_name.as(:typespec_field) |
generic_type.as(:typespec_type)
) >> lang_type.as(:typespec_spec) >> eol
}
rule(:lang_type) {
space? >> lang_type_lexer.as(:lang_type_tokens)
}
rule(:lang_type_lexer) {
(lang_type_word.as(:lang_type_token) | lang_type_separator.as(:lang_type_token)).repeat(1)
}
rule(:lang_type_word) {
match('[a-zA-Z0-9_\-]').repeat(1)
}
rule(:lang_type_separator) {
match('[\<\>\[\]\:\.\,]').repeat(1)
}
rule(:service) {
k_service >>
service_name.as(:service_name) >>
#type_param_decl.maybe.as(:type_param_decl) >>
lt_extend_class.maybe.as(:super_class) >>
k_lwing >>
service_description.as(:service_versions) >>
k_rwing
}
rule(:service_description) {
(func | version_label).repeat.as(:service_description)
}
rule(:version_label) {
# terminal
field_id
}
rule(:func) {
func_modifier.maybe.as(:func_modifier) >>
return_type.as(:return_type) >>
func_name.as(:func_name) >>
k_lparen >>
func_args.as(:func_args) >>
k_rparen >>
throws_classes.maybe.as(:func_throws) >>
eol
}
rule(:func_modifier) {
k_bang.as(:val_override) |
k_minus.as(:val_remove) |
k_plus.as(:val_add)
}
sequence :func_args_seq, :k_comma, :field_element
rule(:func_args) {
func_args_seq
}
sequence :throws_classes_seq, :k_comma, :generic_type, 1
rule(:throws_classes) {
k_throws >> throws_classes_seq
}
rule(:server) {
k_server >>
service_name.as(:server_name) >>
k_lwing >>
scope.repeat.as(:server_body) >>
k_rwing
}
rule(:scope) {
generic_type.as(:scope_type) >>
field_name.as(:scope_name) >>
k_default.maybe.as(:scope_default) >>
eol
}
rule(:lt_extend_class) {
k_lpoint >> generic_type
}
rule(:field_type) {
generic_type.as(:field_type) >> k_question.maybe.as(:field_type_maybe)
}
rule(:return_type) {
field_type
}
rule(:generic_type) {
class_name.as(:generic_type) >> type_param.maybe.as(:type_params)
}
sequence :type_param_seq, :k_comma, :generic_type, 1
rule(:type_param) {
k_lpoint >> type_param_seq >> k_rpoint
}
sequence :type_param_decl_seq, :k_comma, :class_name, 1
rule(:type_param_decl) {
k_lpoint >> type_param_decl_seq >> k_rpoint
}
rule(:literal) {
literal_nil | literal_bool | literal_int | literal_float | literal_str | literal_list | literal_map | literal_const
}
rule(:literal_nil) {
k_nil.as(:literal_nil)
}
rule(:literal_bool) {
k_true.as(:literal_true) | k_false.as(:literal_false)
}
rule(:literal_const) {
const_name.as(:literal_const)
}
rule(:literal_int) {
space? >> (
(str('-') | str('+')).maybe >>
(str('0') | (match('[1-9]') >> match('[0-9]').repeat))
).as(:literal_int) >>
boundary
}
rule(:literal_float) {
space? >> (
(str('0') | (match('[1-9]') >> match('[0-9]').repeat)) >> str('.') >> match('[0-9]').repeat(1)
).as(:literal_float) >>
boundary
}
rule(:literal_str_dq) {
space? >> str('"') >> (
(str('\\') >> any | str('"').absent? >> any ).repeat
).as(:literal_str_dq) >>
str('"')
}
rule(:literal_str_sq) {
space? >> str("'") >> (
(str('\\') >> any | str("'").absent? >> any ).repeat
).as(:literal_str_sq) >>
str("'")
}
rule(:literal_str) {
(literal_str_dq | literal_str_sq).repeat(1).as(:literal_str_seq)
}
sequence :literal_list_seq, :k_comma, :literal
rule(:literal_list) {
space? >> k_lbracket >>
literal_list_seq.as(:literal_list) >>
k_rbracket
}
sequence :literal_map_seq, :k_comma, :literal_map_pair
rule(:literal_map) {
space? >> k_lwing >>
literal_map_seq.as(:literal_map) >>
k_rwing
}
rule(:literal_map_pair) {
literal.as(:literal_map_key) >> k_colon >> literal.as(:literal_map_value)
}
rule(:path) {
# TODO path
space? >> match('[a-zA-Z0-9_\-\.\ ]').repeat(1).as(:path) >> boundary
}
rule(:lang_name) {
name
}
rule(:namespace_name) {
(name >> ((str('.') | str('::')) >> name).repeat).as(:sequence)
}
rule(:const_name) {
name
}
rule(:class_name) {
name
}
rule(:service_name) {
class_name
}
rule(:field_name) {
name
}
rule(:func_name) {
name
}
rule(:name) {
# terminal
space? >> (match('[a-zA-Z]') >> match('[a-zA-Z0-9_]').repeat).as(:name) >> boundary
}
rule(:inline_comment) {
str('/*') >> (
inline_comment | # accepts nested comments
(str('*') >> str('/').absent?) |
(str('*').absent? >> any)
).repeat >> str('*/')
}
rule(:line_comment) {
(str('//') | str('#')) >>
(match('[\r\n]').absent? >> any).repeat >>
match('[\r\n]').repeat(1)
}
rule(:comment) {
inline_comment | line_comment
}
rule(:space) {
(match('[ \r\n\t]') | comment).repeat(1)
}
rule(:space?) {
space.maybe
}
rule(:eol) {
match('[ \t]').repeat >>
(match('[ \t;\r\n]') | line_comment).repeat(1)
}
rule(:boundary) {
match('[a-zA-Z0-9_]').absent?
}
keyword('include')
keyword('namespace')
keyword('message')
keyword('enum')
keyword('exception')
keyword('const')
keyword('typedef')
keyword('typespec')
keyword('service')
keyword('server')
keyword('optional')
keyword('required')
keyword('throws')
keyword('default')
keyword('nil')
keyword('true')
keyword('false')
keyword('void')
separator('*', :k_star)
separator('=', :k_equal)
separator('{', :k_lwing)
separator('}', :k_rwing)
separator('(', :k_lparen)
separator(')', :k_rparen)
separator(':', :k_colon)
separator(',', :k_comma)
separator(';', :k_semi)
separator('<', :k_lpoint)
separator('>', :k_rpoint)
separator('[', :k_lbracket)
separator(']', :k_rbracket)
separator('!', :k_bang)
separator('-', :k_minus)
separator('+', :k_plus)
separator('?', :k_question)
separator('.', :k_dot)
LINE_HEAD_FORMAT = " % 4d: "
LINE_HEAD_SIZE = (LINE_HEAD_FORMAT % 0).size
AFTER_BUFFER = 200
AFTER_LINES = 3
BEFORE_BUFFER = 200
BEFORE_LINES = 4
def print_error(error, fname, out=STDERR)
error_tree = self.root.error_tree
last = error_tree
until last.children.empty?
last = last.children.last
end
last_cause = last.parslet.instance_eval('@last_cause')
source = last_cause.source
row, col = source.line_and_column(last_cause.pos)
old_pos = source.pos
begin
source.pos = last_cause.pos - col + 1
line, *after = source.read(AFTER_BUFFER).to_s.split("\n")
after = after[0,AFTER_LINES]
source.pos = last_cause.pos - col - BEFORE_BUFFER
before = source.read(BEFORE_BUFFER).to_s.split("\n")
before = before[-BEFORE_LINES,BEFORE_LINES] || []
ensure
source.pos = old_pos
end
if m = /[ \t\r\n]*/.match(line)
heading = m[0]
else
heading = ""
end
out.puts "syntax error:"
(
error.to_s.split("\n") +
error_tree.to_s.split("\n")
).each {|ln|
out.puts " "+ln
}
out.puts ""
out.puts "around line #{row} column #{heading.size}-#{col}:"
out.puts ""
before.each_with_index {|ln,i|
l = row - after.size - 1 + i
out.print LINE_HEAD_FORMAT % l
out.puts ln
}
out.print LINE_HEAD_FORMAT % row
out.puts line
out.print " "*LINE_HEAD_SIZE
out.puts heading + '^'*(col - heading.size)
after.each_with_index {|ln,i|
l = row + 1 + i
out.print LINE_HEAD_FORMAT % l
out.puts ln
}
out.puts ""
out
end
end
end
module MessageSpec
class MessageSpecError < StandardError
end
class SyntaxError < MessageSpecError
end
class IncludeError < MessageSpecError
end
class Processor
require 'stringio'
def initialize(search_paths = [])
@parslet = ParsletParser.new
@visitor = Visitor.new
@evaluator = nil
@search_paths = search_paths
@ast = []
end
attr_reader :parslet
attr_reader :visitor
attr_reader :evaluator
attr_reader :ast
def parse(src, fname, dir)
begin
tree = @parslet.parse(src)
ast = @visitor.apply(tree)
rescue Parslet::ParseFailed => error
msg = @parslet.print_error(error, fname, StringIO.new).string
raise SyntaxError, msg
end
ast.each {|e|
if e.class == AST::Include
parse_include(e.path, dir, fname)
else
@ast << e
end
}
self
end
def parse_file(path)
parse(File.read(path), File.basename(path), File.dirname(path))
end
def evaluate
@evaluator ||= Evaluator.new
@evaluator.evaluate(@ast)
self
end
def generate(specs, dir)
# real_type_table
end
protected
def parse_include(inc, dir, fname)
if dir
search_paths = @search_paths + [dir]
else
search_paths = @search_paths
end
search_paths.each {|dir|
real_path = File.join(dir, inc)
if File.file?(real_path)
return parse_file(real_path)
end
}
raise IncludeError, format_include_error(inc, fname)
end
def format_include_error(inc, fname)
[
"#{fname}:",
" Can't include file #{inc}"
].join("\n")
end
end
end
module MessageSpec
module AST
class Element
end
module ValueAssigned
attr_reader :value
end
class Document < Array
end
class Include
def initialize(path)
@path = path
end
attr_reader :path
end
class Namespace < Element
def initialize(scopes, lang)
@scopes = scopes
@lang = lang
end
attr_reader :scopes, :lang
def scope(separator='::')
@scopes.join(separator)
end
def lang_specific?
!!@lang
end
end
class Generics < Element
def initialize(type_params)
@type_params = type_params
end
attr_reader :type_params
end
class Message < Element
def initialize(name, super_class, fields)
@name = name
@super_class = super_class
@fields = fields
end
attr_reader :name, :super_class, :fields
end
class GenericMessage < Generics
def initialize(name, super_class, fields, type_params)
super(type_params)
@name = name
@super_class = super_class
@fields = fields
end
attr_reader :name, :super_class, :fields
end
class Exception < Message
end
class GenericException < GenericMessage
end
class Field < Element
def initialize(field_id, field_type, modifier, name)
@field_id = field_id
@field_type = field_type
@name = name
@modifier = modifier
end
attr_reader :field_id, :field_type, :name
def required?
@modifier == FIELD_REQUIRED
end
def optional?
@modifier == FIELD_OPTIONAL
end
end
class ValueAssignedField < Field
include ValueAssigned
def initialize(field_id, type, modifier, name, value)
@field_id = field_id
@type = type
@modifier = modifier
@name = name
@value = value
end
end
class Enum < Element
def initialize(name, fields)
@name = name
@fields = fields
end
attr_reader :name, :fields
end
class EnumField < Element
def initialize(field_id, name)
@field_id = field_id
@name = name
end
end
class Typedef < Element
def initialize(type, new_type)
@type = type
@new_type = new_type
end
attr_reader :type, :new_type
end
class GenericTypedef < Generics
def initialize(type, new_type, type_params)
super(type_params)
@type = type
@new_type = new_type
end
attr_reader :type, :new_type
end
class Const < Element
include ValueAssigned
def initialize(field_type, name, value)
@field_type = field_type
@name = name
@value = value
end
attr_reader :field_type, :name
end
class FieldSpec < Element
def initialize(name, target_class, target_field, spec)
@name = name
@target_class = target_class
@target_field = target_field
@spec = spec
end
attr_reader :name, :target_class, :target_field, :spec
end
class GenericFieldSpec < Generics
def initialize(name, target_class, target_field, spec, type_params)
super(type_params)
@name = name
@target_class = target_class
@target_field = target_field
@spec = spec
end
attr_reader :name, :target_class, :target_field, :spec
end
class TypeSpec < Element
def initialize(name, target_type, spec)
@name = name
@target_type = target_type
@spec = spec
end
attr_reader :name, :target_type, :spec
end
class GenericTypeSpec < Generics
def initialize(name, target_type, spec, type_params)
super(type_params)
@name = name
@target_type = target_type
@spec = spec
end
attr_reader :name, :target_type, :spec
end
class LangType < Element
def initialize(tokens)
@tokens = tokens
end
attr_reader :tokens
end
class Service < Element
def initialize(name, super_class, versions)
@name = name
@super_class = super_class
@versions = versions
end
attr_reader :name, :super_class, :versions
end
class ServiceVersion < Element
def initialize(version, funcs)
@version = version
@funcs = funcs
end
attr_reader :version, :funcs
end
class Server < Element
def initialize(name, scopes)
@name = name
@scopes = scopes
end
attr_reader :name
attr_reader :scopes
end
class Scope < Element
def initialize(type, name, default)
@type = type
@name = name
@default = default
end
attr_reader :type, :name
def default?
@default
end
end
class Func < Element
def initialize(name, modifier, return_type, args, exceptions)
@name = name
@modifier = modifier
@return_type = return_type
@args = args
@exceptions = exceptions
end
attr_reader :name, :return_type, :args, :exceptions
def override?
@modifier == FUNC_OVERRIDE
end
def remove?
@modifier == FUNC_REMOVE
end
def add?
@modifier == FUNC_ADD
end
attr_reader :modifier
def has_exceptions?
!@exceptions.empty?
end
end
class FieldType
def initialize(type, nullable)
@type = type
@nullable = nullable
end
attr_reader :type
def nullable?
@nullable
end
end
class Type < Element
def initialize(name)
@name = name
end
attr_reader :name
end
class GenericSpecifiedType < Type
def initialize(name, type_params)
super(name)
@type_params = type_params
end
attr_reader :type_params
end
class Literal
end
class ConstLiteral < Literal
def initialize(name)
@name = name
end
end
class IntLiteral < Literal
def initialize(value)
@value = value
end
end
class FlaotLiteral < Literal
def initialize(value)
@value = value
end
end
class NilLiteral < Literal
end
class BoolLiteral < Literal
end
class TrueLiteral < BoolLiteral
end
class FalseLiteral < BoolLiteral
end
class StringLiteral < Literal
def initialize(value)
@value = value
end
end
# TODO container literal
#class ListLiteral < Literal
# def initialize(array)
# @array = array
# end
#end
# TODO container literal
#class MapLiteralPair
# def initialize(k, v)
# @key = k
# @value = v
# end
#end
# TODO container literal
#class MapLiteral < Literal
# def initialize(pairs)
# @pairs = pairs
# end
#end
FIELD_OPTIONAL = :optional
FIELD_REQUIRED = :required
FUNC_OVERRIDE = :override
FUNC_REMOVE = :remove
FUNC_ADD = :add
class Sequence < Array
end
end
class Visitor < Parslet::Transform
rule(:name => simple(:n)) {
n.to_s
}
rule(:sequence_x => simple(:x)) {
x
}
rule(:sequence_xs => simple(:xs)) {
xs
}
rule(:sequence => simple(:x)) {
x ? AST::Sequence.new([x]) : AST::Sequence.new
}
rule(:sequence => sequence(:x)) {
AST::Sequence.new(x)
}
rule(:val_int => simple(:i)) {
i.to_i
}
rule(:val_optional => simple(:x)) {
AST::FIELD_OPTIONAL
}
rule(:val_required => simple(:x)) {
AST::FIELD_REQUIRED
}
rule(:val_override => simple(:x)) {
AST::FUNC_OVERRIDE
}
rule(:val_remove => simple(:x)) {
AST::FUNC_REMOVE
}
rule(:val_add => simple(:x)) {
AST::FUNC_ADD
}
rule(:generic_type => simple(:n),
:type_params => simple(:tp)) {
if tp
AST::GenericSpecifiedType.new(n, tp)
else
AST::Type.new(n)
end
}
rule(:field_id => simple(:i),
:field_modifier => simple(:m),
:field_type => simple(:t),
:field_name => simple(:n),
:field_default => simple(:v)) {
m ||= AST::FIELD_REQUIRED
if v == nil
AST::Field.new(i, t, m, n)
else
AST::ValueAssignedField.new(i, t, m, n, v)
end
}
rule(:field_type => simple(:t),
:field_type_maybe => simple(:n)) {
if n
AST::FieldType.new(t, true)
else
AST::FieldType.new(t, false)
end
}
rule(:lang_type_token => simple(:t)) {
t.to_s
}
rule(:lang_type_tokens => sequence(:ts)) {
AST::LangType.new(ts)
}
rule(:literal_const => simple(:n)) {
AST::ConstLiteral.new(n)
}
rule(:literal_int => simple(:i)) {
AST::IntLiteral.new(i.to_i)
}
rule(:literal_float => simple(:f)) {
AST::FloatLiteral.new(f.to_f)
}
rule(:literal_str_dq => simple(:s)) {
s.to_s.gsub(/\\(.)/) {|e|
eval("\"\\#{$~[1]}\"") # TODO escape
}
}
rule(:literal_str_sq => simple(:s)) {
s.to_s
}
rule(:literal_str_seq => sequence(:ss)) {
AST::StringLiteral.new(ss.join)
}
rule(:literal_nil => simple(:_)) {
AST::NilLiteral.new
}
rule(:literal_true => simple(:_)) {
AST::TrueLiteral.new
}
rule(:literal_false => simple(:_)) {
AST::FalseLiteral.new
}
# TODO container literal
#rule(:literal_list => simple(:a)) {
# AST::ListLiteral.new(Array.new(a))
#}
# TODO container literal
#rule(:literal_map => simple(:ps)) {
# AST::MapLiteral.new(Array.new(ps))
#}
# TODO container literal
#rule(:literal_map_key => simple(:k), :literal_map_value => simple(:v)) {
# AST::MapLiteralPair.new(k, v)
#}
rule(:type_param_decl => simple(:tp),
:message_name => simple(:n),
:message_body => sequence(:b),
:super_class => simple(:sc)) {
if tp
AST::GenericMessage.new(n, sc, b, tp)
else
AST::Message.new(n, sc, b)
end
}
rule(:type_param_decl => simple(:tp),
:exception_name => simple(:n),
:exception_body => sequence(:b),
:super_class => simple(:sc)) {
if tp
AST::GenericException.new(n, sc, b, tp)
else
AST::Exception.new(n, sc, b)
end
}
rule(:const_type => simple(:t),
:const_name => simple(:n),
:const_value => simple(:v)) {
AST::Const.new(t, n, v)
}
rule(:type_param_decl => simple(:tp),
:typedef_type => simple(:t),
:typedef_name => simple(:n)) {
if tp
AST::GenericTypedef.new(t, n, tp)
else
AST::Typedef.new(t, n)
end
}
rule(:type_param_decl => simple(:tp),
:typespec_lang => simple(:n),
:typespec_class => simple(:c),
:typespec_field => simple(:f),
:typespec_spec => simple(:s)) {
if tp
AST::GenericFieldSpec.new(n, c, f, s, tp)
else
AST::FieldSpec.new(n, c, f, s)
end
}
rule(:type_param_decl => simple(:tp),
:typespec_lang => simple(:n),
:typespec_type => simple(:t),
:typespec_spec => simple(:s)) {
if tp
AST::GenericTypeSpec.new(n, t, s, tp)
else
AST::TypeSpec.new(n, t, s)
end
}
rule(:enum_field_id => simple(:i),
:enum_field_name => simple(:n)) {
AST::EnumField.new(i, n)
}
rule(:enum_name => simple(:n),
:enum_body => sequence(:b)) {
AST::Enum.new(n, b)
}
rule(:func_modifier => simple(:m),
:return_type => simple(:rt),
:func_name => simple(:n),
:func_args => simple(:a),
:func_throws => simple(:ex)) {
AST::Func.new(n, m, rt, a, ex)
}
rule(:service_description => sequence(:s)) {
current = AST::ServiceVersion.new(0, [])
versions = [current]
s.each {|l|
case l
when Integer
v = versions.find {|v| v.version == l }
if v
current = v
else
current = AST::ServiceVersion.new(l, [])
versions << current
end
else
current.funcs << l
end
}
versions
}
rule(:service_name => simple(:n),
:service_versions => sequence(:vs),
:super_class => simple(:sc)) {
AST::Service.new(n, sc, vs)
}
rule(:scope_type => simple(:t),
:scope_name => simple(:n),
:scope_default => simple(:d)) {
if d
AST::Scope.new(t, n, true)
else
AST::Scope.new(t, n, false)
end
}
rule(:server_name => simple(:n),
:server_body => sequence(:b)) {
AST::Server.new(n, b)
}
rule(:namespace_name => simple(:n)) {
AST::Namespace.new(n, nil)
}
rule(:namespace_name => simple(:n),
:namespace_lang => simple(:l)) {
AST::Namespace.new(n, l)
}
rule(:path => simple(:n)) {
n
}
rule(:include => simple(:n)) {
AST::Include.new(n.to_s)
}
rule(:document => sequence(:es)) {
AST::Document.new(es)
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment