Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
806 lines (738 sloc) 17.9 KB
// IDL parser
// Generate using:
// go tool yacc -o parseidl.go parseidl.y
%{
// IDL Parser
// Generated by:
// go tool yacc -o parseidl.go parseidl.y
//
// THIS FILE IS AUTO-GENERATED - DO NOT MODIFY
package parser
import (
"github.com/babelrpc/babel/idl"
"fmt"
"io"
"os"
"path"
"path/filepath"
"strconv"
"strings"
"text/scanner"
"unicode/utf8"
)
// globalData holds information used for parsing that is held in the lexer.
type globalData struct {
pidl *idl.Idl
currentConst *idl.Const
currentEnum *idl.Enum
currentStruct *idl.Struct
currentService *idl.Service
currentMethod *idl.Method
basedir string
}
%}
%union {
Ident string
String string
Char rune
Int int64
Float float64
Bool bool
Comment string
DataType *idl.Type
Comments []string
Attr *idl.Attribute
Attrs []*idl.Attribute
AttrVal *idl.Pair
AttrVals []*idl.Pair
Initializer *idl.Pair
As string
}
%token<Ident> IDENT
%token<String> STRING
%token<Char> CHAR
%token<Int> INT
%token<Float> FLOAT
%token<Bool> BOOL
%token<Comment> COMMENT
%token<Ident> BINARY
// header tokens
%token IMPORT NAMESPACE
// language tokens
%token<Ident> LANG
// Definition tokens
%token<Ident> CONST ENUM STRUCT EXTENDS SERVICE ABSTRACT
// Data type tokens
%token<Ident> BASETYPE LIST MAP AS VOID
%type<Ident> Language
%type<DataType> Type
%type<Comments> DocComments
%type<Comment> DocComment
%type<Attr> Attribute
%type<Attrs> Attributes
%type<Attrs> AttrList
%type<Attrs> AttrLists
%type<AttrVal> AttrValue
%type<AttrVals> AttrValues
%type<Initializer> OptInitializer
%type<As> OptionalAs
%type<DataType> TypeOrVoid
%type<Bool> OptionalAbstract
%type<Ident> AttrName
%type<Ident> PathName
%start IDL
%%
IDL :
DocComments
Imports
DefaultNamespace
Namespaces
Definitions
{
yylex.(*IdlLex).globals.pidl.Comments = $1
}
;
Imports : | Imports Import ;
Import :
IMPORT STRING CommaSemiOptional
{
//fmt.Printf("import \"%s\"\n", $2)
fpath := path.Join(yylex.(*IdlLex).globals.basedir, $2)
fname := filepath.FromSlash(fpath)
f, err := os.Open(fname)
check(err, true, yylex)
defer f.Close()
var lexer IdlLex
lexer.Init(f, f.Name())
lexer.globals.pidl, err = yylex.(*IdlLex).globals.pidl.AddImport(fpath)
check(err, true, yylex)
lexer.globals.basedir = filepath.ToSlash(filepath.Dir(fname))
yyParse(&lexer)
yylex.(*IdlLex).Errors = append(yylex.(*IdlLex).Errors, lexer.Errors...)
}
;
Namespaces : | Namespaces Namespace ;
Namespace :
NAMESPACE Language STRING CommaSemiOptional
{
// fmt.Printf("namespace %s \"%s\"\n", $2, $3)
check(yylex.(*IdlLex).globals.pidl.AddNamespace($2, $3), false, yylex)
}
;
DefaultNamespace:
NAMESPACE AttrName '/' PathName CommaSemiOptional
{
// fmt.Printf("namespace %s \"%s\"\n", $2, $3)
check(yylex.(*IdlLex).globals.pidl.AddDefaultNamespace($2, $4), false, yylex)
}
;
PathName :
IDENT
{
$$ = $1
}
| PathName '/' IDENT
{
$$ = $1 + "/" + $3
}
;
Language : LANG ;
Definitions: | Definitions Definition ;
Definition :
DocComments CONST IDENT '{'
{
//fmt.Printf("const %s {\n", $2)
var err error
yylex.(*IdlLex).globals.currentConst, err = yylex.(*IdlLex).globals.pidl.AddConst($3)
check(err, true, yylex)
yylex.(*IdlLex).globals.currentConst.Comments = $1
}
Constants '}'
{
//fmt.Printf("}\n")
yylex.(*IdlLex).globals.currentConst = nil
}
| DocComments ENUM IDENT '{'
{
//fmt.Printf("enum %s {\n", $2)
var err error
yylex.(*IdlLex).globals.currentEnum, err = yylex.(*IdlLex).globals.pidl.AddEnum($3)
check(err, true, yylex)
yylex.(*IdlLex).globals.currentEnum.Comments = $1
}
Enums '}'
{
//fmt.Printf("}\n")
yylex.(*IdlLex).globals.currentEnum = nil
}
| DocComments AttrLists OptionalAbstract STRUCT IDENT EXTENDS IDENT '{'
{
//fmt.Printf("struct %s extends %s {\n", $5, $7)
var err error
yylex.(*IdlLex).globals.currentStruct, err = yylex.(*IdlLex).globals.pidl.AddStruct($5)
check(err, true, yylex)
yylex.(*IdlLex).globals.currentStruct.Extends = $7
yylex.(*IdlLex).globals.currentStruct.Comments = $1
yylex.(*IdlLex).globals.currentStruct.Attributes = $2
yylex.(*IdlLex).globals.currentStruct.Abstract = $3
}
Fields '}'
{
//fmt.Printf("}\n")
yylex.(*IdlLex).globals.currentStruct = nil
}
| DocComments AttrLists OptionalAbstract STRUCT IDENT '{'
{
//fmt.Printf("struct %s {\n", $5)
var err error
yylex.(*IdlLex).globals.currentStruct, err = yylex.(*IdlLex).globals.pidl.AddStruct($5)
check(err, true, yylex)
yylex.(*IdlLex).globals.currentStruct.Comments = $1
yylex.(*IdlLex).globals.currentStruct.Attributes = $2
yylex.(*IdlLex).globals.currentStruct.Abstract = $3
}
Fields '}'
{
//fmt.Printf("}\n")
yylex.(*IdlLex).globals.currentStruct = nil
}
| DocComments AttrLists SERVICE IDENT '{'
{
//fmt.Printf("struct %s {\n", $4)
var err error
yylex.(*IdlLex).globals.currentService, err = yylex.(*IdlLex).globals.pidl.AddService($4)
check(err, true, yylex)
yylex.(*IdlLex).globals.currentService.Comments = $1
yylex.(*IdlLex).globals.currentService.Attributes = $2
}
Methods '}'
{
//fmt.Printf("}\n")
yylex.(*IdlLex).globals.currentService = nil
}
;
OptionalAbstract :
{
$$ = false
}
| ABSTRACT
{
$$ = true
}
;
Constants : | Constants Constant ;
Constant :
IDENT '=' INT CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, $3, "int"), false, yylex)
// fmt.Printf("\t%s = %d\n", $1, $3)
}
| IDENT '=' '-' INT CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, -$4, "int"), false, yylex)
// fmt.Printf("\t%s = %d\n", $1, $3)
}
| IDENT '=' FLOAT CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, $3, "float"), false, yylex)
//fmt.Printf("\t%s = %f\n", $1, $3)
}
| IDENT '=' '-' FLOAT CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, -$4, "float"), false, yylex)
//fmt.Printf("\t%s = %f\n", $1, $3)
}
| IDENT '=' STRING CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, $3, "string"), false, yylex)
//fmt.Printf("\t%s = \"%s\"\n", $1, $3)
}
| IDENT '=' BOOL CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, $3, "bool"), false, yylex)
//fmt.Printf("\t%s = \"%s\"\n", $1, $3)
}
| IDENT '=' CHAR CommaSemiOptional
{
check(yylex.(*IdlLex).globals.currentConst.Add($1, $3, "char"), false, yylex)
//fmt.Printf("\t%s = \'%c\'\n", $1, $3)
}
;
Enums : | Enums Enum ;
Enum :
IDENT '=' INT CommaOptional
{
//fmt.Printf("\t%s = %d\n", $1, $3)
check(yylex.(*IdlLex).globals.currentEnum.Add($1, $3), false, yylex)
}
| IDENT '=' '-' INT CommaOptional
{
//fmt.Printf("\t%s = %d\n", $1, $3)
check(yylex.(*IdlLex).globals.currentEnum.Add($1, -$4), false, yylex)
}
;
Fields : | Fields Field ;
Field :
DocComments AttrLists Type IDENT OptInitializer CommaSemiOptional
{
//fmt.Printf("\t%s %s\n", $3, $4)
$3.Rename = $4
f, err := yylex.(*IdlLex).globals.currentStruct.AddField($3, $4)
if check(err, false, yylex) {
f.Comments = $1
f.Attributes = $2
if $5 != nil {
check(f.SetInitializer($5.Value, $5.DataType), false, yylex)
}
}
}
;
Methods : | Methods Method ;
Method :
DocComments AttrLists TypeOrVoid IDENT '('
{
//fmt.Printf("\t%s %s\n", $3, $4)
var err error
yylex.(*IdlLex).globals.currentMethod, err = yylex.(*IdlLex).globals.currentService.AddMethod($3, $4)
check(err, true, yylex)
yylex.(*IdlLex).globals.currentMethod.Comments = $1
yylex.(*IdlLex).globals.currentMethod.Attributes = $2
}
Parameters ')' CommaSemiOptional
{
yylex.(*IdlLex).globals.currentMethod = nil
}
;
TypeOrVoid:
VOID
{
$$ = &idl.Type{Name: "void"}
}
| Type
{
$$ = $1
}
;
Parameters : | Parameters Parameter ;
Parameter :
DocComments AttrLists Type IDENT OptInitializer CommaOptional
{
//fmt.Printf("\t%s %s\n", $3, $4)
$3.Rename = $4
p, err := yylex.(*IdlLex).globals.currentMethod.AddParameter($3, $4)
if check(err, false, yylex) {
p.Comments = $1
p.Attributes = $2
if $5 != nil {
check(p.SetInitializer($5.Value, $5.DataType), false, yylex)
}
}
}
;
Type :
BASETYPE
{
$$ = &idl.Type{Name: $1}
}
| IDENT
{
$$ = &idl.Type{Name: $1}
}
| BINARY
{
$$ = &idl.Type{Name: $1}
}
| LIST '<' Type OptionalAs '>'
{
$3.Rename = $4
$$ = &idl.Type{Name: "list", ValueType: $3}
}
| MAP '<' BASETYPE OptionalAs ',' Type OptionalAs '>'
{
$6.Rename = $7
$$ = &idl.Type{Name: "map", KeyType: &idl.Type{Name: $3, Rename: $4}, ValueType: $6}
}
;
OptionalAs :
{
$$ = ""
}
| AS STRING
{
$$ = $2
}
;
OptInitializer :
{
$$ = nil
}
| '=' INT
{
$$ = &idl.Pair{Value: $2, DataType: "int"}
}
| '=' '-' INT
{
$$ = &idl.Pair{Value: -$3, DataType: "int"}
}
| '=' FLOAT
{
$$ = &idl.Pair{Value: $2, DataType: "float"}
}
| '=' '-' FLOAT
{
$$ = &idl.Pair{Value: -$3, DataType: "float"}
}
| '=' STRING
{
$$ = &idl.Pair{Value: $2, DataType: "string"}
}
| '=' BOOL
{
$$ = &idl.Pair{Value: $2, DataType: "bool"}
}
| '=' CHAR
{
$$ = &idl.Pair{Value: $2, DataType: "char"}
}
| '=' IDENT '.' IDENT
{
$$ = &idl.Pair{Value: $2 + "." + $4, DataType: "#ref"}
}
;
AttrLists :
{
$$ = make([]*idl.Attribute, 0)
}
| AttrLists AttrList
{
for i, _ := range($2) {
for j := i + 1; j < len($2); j++ {
if strings.ToLower($2[i].Name) == strings.ToLower($2[j].Name) && $2[i].Scope == "" && $2[j].Scope == "" {
yylex.Error(fmt.Sprintf("Attribute used twice: %s", $2[j].Name))
}
}
}
for _, a1 := range($1) {
for _, a2 := range($2) {
if strings.ToLower(a1.Name) == strings.ToLower(a2.Name) && a1.Scope == "" && a2.Scope == "" {
yylex.Error(fmt.Sprintf("Attribute used twice: %s", a2.Name))
}
}
}
$$ = append($1, $2...)
}
;
AttrList :
'[' Attributes ']'
{
// fmt.Printf("]\n")
$$ = $2
}
| '@' IDENT '[' Attributes ']'
{
// fmt.Printf("]\n")
for _, a := range($4) {
a.Scope = $2
}
$$ = $4
}
;
Attributes :
{
$$ = make([]*idl.Attribute, 0)
}
| Attributes Attribute
{
//for _, a := range($1) {
// if strings.ToLower(a.Name) == strings.ToLower($2.Name) && a.Scope == "" && $2.Scope == "" {
// yylex.Error(fmt.Sprintf("Attribute used twice: %s", $2.Name))
// }
//}
$$ = append($1, $2)
}
;
Attribute :
AttrName CommaOptional
{
//fmt.Printf("%s ", $1)
$$ = &idl.Attribute{Name: $1, Parameters: make([]*idl.Pair, 0)}
}
| AttrName '(' AttrValues ')' CommaOptional
{
//fmt.Printf(") ")
$$ = &idl.Attribute{Name: $1, Parameters: $3}
}
;
AttrName :
IDENT
{
$$ = $1
}
| AttrName '.' IDENT
{
$$ = $1 + "." + $3
}
;
AttrValues :
{
$$ = make([]*idl.Pair, 0)
}
| AttrValues AttrValue
{
$$ = append($1, $2)
}
;
AttrValue :
INT CommaOptional
{
//fmt.Printf("%d ", $1)
$$ = &idl.Pair{Value: $1, DataType: "int"}
}
| '-' INT CommaOptional
{
//fmt.Printf("%d ", $1)
$$ = &idl.Pair{Value: -$2, DataType: "int"}
}
| FLOAT CommaOptional
{
//fmt.Printf("%f ", $1)
$$ = &idl.Pair{Value: $1, DataType: "float"}
}
| '-' FLOAT CommaOptional
{
//fmt.Printf("%f ", $1)
$$ = &idl.Pair{Value: -$2, DataType: "float"}
}
| STRING CommaOptional
{
//fmt.Printf("\"%s\" ", $1)
$$ = &idl.Pair{Value: $1, DataType: "string"}
}
| BOOL CommaOptional
{
//fmt.Printf("\"%d\" ", $1)
$$ = &idl.Pair{Value: $1, DataType: "bool"}
}
| CHAR CommaOptional
{
//fmt.Printf("\"%d\" ", $1)
$$ = &idl.Pair{Value: $1, DataType: "char"}
}
| AttrName CommaOptional
{
//fmt.Printf("\"%d\" ", $1)
$$ = &idl.Pair{Value: $1, DataType: "#ref"}
}
| IDENT '=' INT CommaOptional
{
//fmt.Printf("%s = %d ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: $3, DataType: "int"}
}
| IDENT '=' '-' INT CommaOptional
{
//fmt.Printf("%s = %d ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: -$4, DataType: "int"}
}
| IDENT '=' FLOAT CommaOptional
{
//fmt.Printf("%s = %f ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: $3, DataType: "float"}
}
| IDENT '=' '-' FLOAT CommaOptional
{
//fmt.Printf("%s = %f ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: -$4, DataType: "float"}
}
| IDENT '=' STRING CommaOptional
{
//fmt.Printf("%s = \"%s\" ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: $3, DataType: "string"}
}
| IDENT '=' BOOL CommaOptional
{
//fmt.Printf("%s = \"%d\" ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: $3, DataType: "bool"}
}
| IDENT '=' CHAR CommaOptional
{
//fmt.Printf("%s = \"%d\" ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: $3, DataType: "char"}
}
| IDENT '=' AttrName CommaOptional
{
//fmt.Printf("%s = \"%d\" ", $1, $3)
$$ = &idl.Pair{Name: $1, Value: $3, DataType: "#ref"}
}
;
CommaOptional : | ',' ;
CommaSemiOptional : | ',' | ';' ;
DocComments :
{
$$ = make([]string, 0)
}
| DocComments DocComment
{
$$ = append($1, $2)
// fmt.Printf("*** %s\n", $2)
}
;
DocComment : COMMENT
{
//fmt.Printf(" %s\n", $1)
}
;
%%
// IdlLex is a lexer usable by yacc that uses Go's built-in lexer
// to provide lexical analysis for IDL files.
type IdlLex struct {
s scanner.Scanner
Filename string
Errors []string
globals globalData
}
// Lex returns the next token in the steam and classifies it.
func (lex *IdlLex) Lex(yylval *yySymType) int {
var err error
again:
tok := lex.s.Scan()
if tok == scanner.EOF {
return 0
}
// fmt.Printf("lex: %s %s\n", scanner.TokenString(tok), lex.s.TokenText())
switch scanner.TokenString(tok) {
case "Ident":
yylval.Ident = lex.s.TokenText()
switch yylval.Ident {
case "import":
return IMPORT
case "namespace":
return NAMESPACE
case "csharp", "java", "python", "ruby", "go", "js", "asp", "php", "ios":
return LANG
case "const":
return CONST
case "enum":
return ENUM
case "abstract":
return ABSTRACT
case "struct":
return STRUCT
case "extends":
return EXTENDS
case "void":
return VOID;
case "service":
return SERVICE
case "bool", "byte", "int8", "int16", "int32", "int64", "float32", "float64", "string", "datetime", "decimal", "char":
return BASETYPE
case "binary":
return BINARY
case "list":
return LIST
case "map":
return MAP
case "as":
return AS
case "true", "false":
yylval.Bool, err = strconv.ParseBool(lex.s.TokenText())
if err != nil {
lex.Error(fmt.Sprintf("Unable to parse bool %s: %s", lex.s.TokenText(), err))
return 0
}
return BOOL
}
return IDENT
case "String":
//yylval.String = strings.Trim(lex.s.TokenText(), "\"")
yylval.String, err = strconv.Unquote(lex.s.TokenText())
if err != nil {
lex.Error(err.Error())
}
return STRING
case "Char":
s, err := strconv.Unquote(lex.s.TokenText())
if err != nil {
lex.Error(err.Error())
}
if utf8.RuneCountInString(s) == 1 {
yylval.Char, _ = utf8.DecodeRuneInString(s)
return CHAR
} else {
lex.Error(fmt.Sprintf("Character value shouldn't contain multiple characters: %s", lex.s.TokenText()))
}
case "Int":
yylval.Int, err = strconv.ParseInt(lex.s.TokenText(), 10, 64)
if err != nil {
lex.Error(fmt.Sprintf("Unable to parse integer %s: %s", lex.s.TokenText(), err))
return 0
}
return INT
case "Float":
yylval.Float, err = strconv.ParseFloat(lex.s.TokenText(), 64)
if err != nil {
lex.Error(fmt.Sprintf("Unable to parse integer %s: %s", lex.s.TokenText(), err))
return 0
}
return FLOAT
case "Comment":
str := lex.s.TokenText()
yylval.Comment = strings.Replace(strings.Trim(str, "/*"), "\r", "", -1)
// only deal with doc comments
if strings.HasPrefix(str, "///") || strings.HasPrefix(str, "/**") {
return COMMENT
} else {
goto again
}
}
return int(tok)
}
// Error is called when a parsing error occcurs. Errors are collected in an array.
func (lex *IdlLex) Error(s string) {
p := lex.s.Pos()
lex.Errors = append(lex.Errors, fmt.Sprintf("Line %d, col %d of %s: %s", p.Line, p.Column, lex.Filename, s))
}
// Init prepares the lexer for use.
func (lex *IdlLex) Init(src io.Reader, fname string) {
lex.s.Init(src)
lex.s.Mode = scanner.ScanChars | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanIdents | scanner.ScanComments
lex.Filename = fname
lex.Errors = make([]string, 0)
}
// check tests for errors logs them to lex's Error function. Fatal
// errors result in a panic.
func check(err error, fatal bool, lex yyLexer) bool {
if err != nil {
m := map[bool]string { false: "Error", true: "Fatal" }
lex.Error(m[fatal] + ": " + err.Error())
if fatal {
panic(fmt.Errorf("%s", strings.Join(lex.(*IdlLex).Errors, "\n")))
}
return false
} else {
return true
}
}
// ParseIdl parses the idl in the given file with tests for the given
// language. The Idl object is returned unless an error occured.
func ParseIdl(fileName, lang string) (*idl.Idl, error) {
f, err := os.Open(fileName)
if err != nil {
return nil, fmt.Errorf("Error opening input file: %s", err)
}
defer f.Close()
var lexer IdlLex
lexer.Init(f, f.Name())
lexer.globals.pidl = new(idl.Idl)
lexer.globals.pidl.Init()
lexer.globals.pidl.Filename = filepath.ToSlash(fileName)
lexer.globals.basedir = filepath.ToSlash(filepath.Dir(fileName))
yyParse(&lexer)
if len(lexer.Errors) > 0 {
return nil, fmt.Errorf("%s\n%d parsing errors, exiting", strings.Join(lexer.Errors, "\n"), len(lexer.Errors))
}
err = lexer.globals.pidl.Validate(lang)
if err != nil {
return nil, fmt.Errorf("IDL does not validate: %s", err)
}
return lexer.globals.pidl, nil
}
You can’t perform that action at this time.