///|
priv struct Parser {
tokens : Array[Token]
mut index : Int
}
///|
fn Parser::current(self : Parser) -> Token {
self.tokens[self.index]
}
///|
fn Parser::advance(self : Parser) -> Token {
let token = self.current()
if !(token.kind is End) {
self.index += 1
}
token
}
///|
fn Parser::unexpected(self : Parser) -> Unit raise IdlError {
let token = self.current()
if token.kind is End {
raise UnexpectedEnd(token.span)
}
raise UnexpectedToken(token.describe(), token.span)
}
///|
fn Parser::eat_symbol(self : Parser, expected : Char) -> Bool {
if self.current().kind is Symbol(actual) && actual == expected {
ignore(self.advance())
true
} else {
false
}
}
///|
fn Parser::expect_symbol(self : Parser, expected : Char) -> Unit raise IdlError {
if !self.eat_symbol(expected) {
self.unexpected()
}
}
///|
fn Parser::is_word(self : Parser, expected : String) -> Bool {
self.current().kind is Identifier(actual) && actual == expected
}
///|
fn Parser::eat_word(self : Parser, expected : String) -> Bool {
if self.is_word(expected) {
ignore(self.advance())
true
} else {
false
}
}
///|
fn Parser::expect_name(self : Parser) -> (String, Span) raise IdlError {
match self.advance() {
{ kind: Identifier(value), span, } => (value, span)
token => raise UnexpectedToken(token.describe(), token.span)
}
}
///|
fn Parser::expect_string(self : Parser) -> (String, Span) raise IdlError {
match self.advance() {
{ kind: StringLiteral(value), span, } => (value, span)
token => raise UnexpectedToken(token.describe(), token.span)
}
}
///|
fn Parser::separator(self : Parser) -> Unit {
if !self.eat_symbol(',') {
ignore(self.eat_symbol(';'))
}
}
///|
fn Parser::parse_type(self : Parser) -> TypeRef raise IdlError {
let (name, _) = self.expect_name()
match name {
"bool" => Base(Bool)
"byte" | "i8" => Base(Byte)
"i16" => Base(I16)
"i32" => Base(I32)
"i64" => Base(I64)
"double" => Base(Double)
"string" => Base(String)
"binary" => Base(Binary)
"void" => Base(Void)
"list" => {
self.expect_symbol('<')
let element = self.parse_type()
self.expect_symbol('>')
List(element)
}
"set" => {
self.expect_symbol('<')
let element = self.parse_type()
self.expect_symbol('>')
Set(element)
}
"map" => {
self.expect_symbol('<')
let key = self.parse_type()
self.expect_symbol(',')
let value = self.parse_type()
self.expect_symbol('>')
Map(key, value)
}
named => Named(named)
}
}
///|
fn Parser::parse_const_value(self : Parser) -> ConstValue raise IdlError {
match self.advance() {
{ kind: StringLiteral(value), .. } => StringValue(value)
{ kind: IntegerLiteral(value), .. } => IntegerValue(value)
{ kind: FloatLiteral(value), .. } => DoubleValue(value)
{ kind: Identifier("true"), .. } => BoolValue(true)
{ kind: Identifier("false"), .. } => BoolValue(false)
{ kind: Identifier(value), .. } => IdentifierValue(value)
{ kind: Symbol('['), .. } => {
let values : Array[ConstValue] = []
while !self.eat_symbol(']') {
values.push(self.parse_const_value())
self.separator()
}
ListValue(values)
}
{ kind: Symbol('{'), .. } => {
let entries : Array[(ConstValue, ConstValue)] = []
while !self.eat_symbol('}') {
let key = self.parse_const_value()
if !self.eat_symbol(':') {
self.expect_symbol('=')
}
let value = self.parse_const_value()
entries.push((key, value))
self.separator()
}
MapValue(entries)
}
token => raise UnexpectedToken(token.describe(), token.span)
}
}
///|
fn Parser::parse_annotations(self : Parser) -> Array[Annotation] raise IdlError {
let annotations : Array[Annotation] = []
if !self.eat_symbol('(') {
return annotations
}
while !self.eat_symbol(')') {
let (key, _) = self.expect_name()
let value = if self.eat_symbol('=') {
match self.advance() {
{ kind: StringLiteral(value) | Identifier(value), .. } => Some(value)
{ kind: IntegerLiteral(value), .. } => Some(value.to_string())
token => raise UnexpectedToken(token.describe(), token.span)
}
} else {
None
}
annotations.push({ key, value, })
self.separator()
}
annotations
}
///|
fn Parser::parse_field(self : Parser) -> Field raise IdlError {
let id_token = self.advance()
let id = match id_token.kind {
IntegerLiteral(value) => value.to_int()
_ => raise UnexpectedToken(id_token.describe(), id_token.span)
}
self.expect_symbol(':')
let requiredness = if self.eat_word("required") {
Required
} else if self.eat_word("optional") {
Optional
} else {
Default
}
let ty = self.parse_type()
let (name, _) = self.expect_name()
let default_value = if self.eat_symbol('=') {
Some(self.parse_const_value())
} else {
None
}
let annotations = self.parse_annotations()
self.separator()
{
id,
requiredness,
ty,
name,
default_value,
annotations,
span: id_token.span,
}
}
///|
fn Parser::parse_fields(
self : Parser,
close : Char,
) -> Array[Field] raise IdlError {
let fields : Array[Field] = []
while !self.eat_symbol(close) {
fields.push(self.parse_field())
}
fields
}
///|
fn Parser::parse_header(self : Parser) -> Header raise IdlError {
let keyword = self.advance()
match keyword.kind {
Identifier("include") => {
let (path, _) = self.expect_string()
Include(path~, span=keyword.span)
}
Identifier("cpp_include") => {
let (path, _) = self.expect_string()
CppInclude(path~, span=keyword.span)
}
Identifier("namespace") => {
let language = match self.advance() {
{ kind: Identifier(value), .. } => value
{ kind: Symbol('*'), .. } => "*"
token => raise UnexpectedToken(token.describe(), token.span)
}
let (name, _) = self.expect_name()
Namespace(language~, name~, span=keyword.span)
}
_ => raise UnexpectedToken(keyword.describe(), keyword.span)
}
}
///|
fn Parser::parse_enum(self : Parser, start : Span) -> Definition raise IdlError {
let (name, _) = self.expect_name()
self.expect_symbol('{')
let members : Array[EnumMember] = []
let mut next_value = 0
while !self.eat_symbol('}') {
let (member_name, span) = self.expect_name()
let value = if self.eat_symbol('=') {
match self.advance() {
{ kind: IntegerLiteral(value), .. } => value.to_int()
token => raise UnexpectedToken(token.describe(), token.span)
}
} else {
next_value
}
next_value = value + 1
let annotations = self.parse_annotations()
self.separator()
members.push({ name: member_name, value, annotations, span, })
}
let annotations = self.parse_annotations()
Enum(name~, members~, annotations~, span=start)
}
///|
fn Parser::parse_record(
self : Parser,
kind : String,
start : Span,
) -> Definition raise IdlError {
let (name, _) = self.expect_name()
self.expect_symbol('{')
let fields = self.parse_fields('}')
let annotations = self.parse_annotations()
match kind {
"struct" => Struct(name~, fields~, annotations~, span=start)
"union" => Union(name~, fields~, annotations~, span=start)
_ => Exception(name~, fields~, annotations~, span=start)
}
}
///|
fn Parser::parse_function(self : Parser) -> FunctionDef raise IdlError {
let start = self.current().span
let oneway = self.eat_word("oneway")
let return_type = self.parse_type()
let (name, _) = self.expect_name()
self.expect_symbol('(')
let arguments = self.parse_fields(')')
let throws = if self.eat_word("throws") {
self.expect_symbol('(')
self.parse_fields(')')
} else {
[]
}
let annotations = self.parse_annotations()
self.separator()
{ name, return_type, arguments, throws, oneway, annotations, span: start, }
}
///|
fn Parser::parse_service(
self : Parser,
start : Span,
) -> Definition raise IdlError {
let (name, _) = self.expect_name()
let extends = if self.eat_word("extends") {
let (base, _) = self.expect_name()
Some(base)
} else {
None
}
self.expect_symbol('{')
let functions : Array[FunctionDef] = []
while !self.eat_symbol('}') {
functions.push(self.parse_function())
}
let annotations = self.parse_annotations()
Service(name~, extends~, functions~, annotations~, span=start)
}
///|
fn Parser::parse_definition(self : Parser) -> Definition raise IdlError {
let keyword = self.advance()
match keyword.kind {
Identifier("const") => {
let ty = self.parse_type()
let (name, _) = self.expect_name()
self.expect_symbol('=')
let value = self.parse_const_value()
self.separator()
Const(name~, ty~, value~, span=keyword.span)
}
Identifier("typedef") => {
let target = self.parse_type()
let (name, _) = self.expect_name()
let annotations = self.parse_annotations()
self.separator()
Typedef(name~, target~, annotations~, span=keyword.span)
}
Identifier("enum") => self.parse_enum(keyword.span)
Identifier("struct") => self.parse_record("struct", keyword.span)
Identifier("union") => self.parse_record("union", keyword.span)
Identifier("exception") => self.parse_record("exception", keyword.span)
Identifier("service") => self.parse_service(keyword.span)
_ => raise UnexpectedToken(keyword.describe(), keyword.span)
}
}
///|
/// Parses a complete Apache Thrift IDL document.
pub fn parse_idl(
input : String,
source? : String = "",
) -> Schema raise IdlError {
let parser = { tokens: lex(input, source~), index: 0, }
let headers : Array[Header] = []
while parser.is_word("include") ||
parser.is_word("cpp_include") ||
parser.is_word("namespace") {
headers.push(parser.parse_header())
parser.separator()
}
let definitions : Array[Definition] = []
while !(parser.current().kind is End) {
definitions.push(parser.parse_definition())
}
{ source, headers, definitions, }
}