//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 30 Jan 06 Brian Frank Creation
// 06 Jul 07 Brian Frank Port from Java
//
**
** TypeParser is used to parser formal type signatures into CTypes.
**
** x::N
** x::V[]
** x::V[x::K]
** |x::A, ... -> x::R|
**
class TypeParser
{
//////////////////////////////////////////////////////////////////////////
// Factory
//////////////////////////////////////////////////////////////////////////
**
** Parse the signature into a resolved CType. We *don't*
** use the CNamespace's cache - it is using me when a signature
** isn't found in the cache. But we do use the CPod's type cache
** via CPod.resolveType.
**
public static CType resolve(CNamespace ns, Str sig)
{
// if last char is ? then parse as nullable
last := sig[-1]
if (last == '?') return resolve(ns, sig[0..-2]).toNullable
// if the last character isn't ] or |, then this a non-generic
// type and we don't even need to allocate a parser
if (last != ']' && last != '|')
{
colon := sig.index("::")
podName := sig[0..<colon]
typeName := sig[colon+2..-1]
return ns.resolvePod(podName, null).resolveType(typeName, true)
}
// we got our work cut out for us - create parser
return TypeParser(ns, sig).loadTop
}
//////////////////////////////////////////////////////////////////////////
// Constructor
//////////////////////////////////////////////////////////////////////////
private new make(CNamespace ns, Str sig)
{
this.ns = ns
this.sig = sig
this.len = sig.size
this.pos = 0
this.cur = sig[pos]
this.peek = sig[pos+1]
}
//////////////////////////////////////////////////////////////////////////
// Parse
//////////////////////////////////////////////////////////////////////////
private CType loadTop()
{
t := loadAny
if (cur != 0) throw err
return t
}
private CType loadAny()
{
CType? t
// |...| is function
if (cur == '|')
t = loadFunc
// [ is either [ffi]xxx or [K:V] map
else if (cur == '[')
{
ffi := true
for (i:=pos+1; i<len; ++i)
{
ch := sig[i]
if (isIdChar(ch)) continue
ffi = (ch == ']')
break
}
if (ffi)
t = loadFFI
else
t = loadMap
}
// otherwise must be basic[]
else
t = loadBasic
// nullable
if (cur == '?')
{
consume('?')
t = t.toNullable
}
// anything left must be series of [] or []?
while (cur == '[')
{
consume('[')
consume(']')
t = t.toListOf
if (cur == '?')
{
consume('?')
t = t.toNullable
}
}
return t
}
private CType loadMap()
{
consume('[')
key := loadAny
consume(':')
val := loadAny
consume(']')
return MapType(key, val)
}
private CType loadFunc()
{
consume('|')
params := CType[,]
names := Str[,]
if (cur != '-')
{
while (true)
{
params.add(loadAny)
names.add(('a'+names.size).toChar)
if (cur == '-') break
consume(',')
}
}
consume('-')
consume('>')
ret := loadAny
consume('|')
return FuncType(params, names, ret)
}
private CType loadFFI()
{
// [java]foo.bar.foo
start := pos
while (cur != ':' || peek != ':') consume
//podName := sig[start..<pos]
consume(':')
consume(':')
// Baz or [Baz
//start = pos
while (cur == '[') consume
while (isIdChar(cur)) consume
//typeName := sig[start..<pos]
qname := sig[start..<pos]
return ns.resolveType(qname);
}
private CType loadBasic()
{
start := pos
while (cur != ':' || peek != ':') consume
consume(':')
consume(':')
while (isIdChar(cur)) consume
qname := sig[start..<pos]
return ns.resolveType(qname)
}
//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////
private Void consume(Int? expected := null)
{
if (expected != null && cur != expected) throw err
cur = peek
pos++
peek = pos+1 < len ? sig[pos+1] : 0
}
private static Bool isIdChar(Int ch)
{
ch.isAlphaNum || ch == '_'
}
private ArgErr err()
{
ArgErr("Invalid type signature '" + sig + "'")
}
//////////////////////////////////////////////////////////////////////////
// Fields
//////////////////////////////////////////////////////////////////////////
private CNamespace ns // namespace we are loading from
private Str sig // signature being parsed
private Int len // length of sig
private Int pos // index of cur in sig
private Int cur // cur character; sig[pos]
private Int peek // next character; sig[pos+1]
}