//
// Copyright (c) 2006, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 5 Jun 06 Brian Frank Creation
//
**
** ScanForUsingsAndTypes is the first phase in a two pass parser. Here
** we scan thru the tokens to parse using declarations and type definitions
** so that we can fully define the namespace of types. The result of this
** step is to populate each CompilationUnit's using and types, and the
** PodDef.typeDefs map.
**
class ScanForUsingsAndTypes : CompilerStep
{
//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////
**
** Constructor takes the associated Compiler
**
new make(Compiler compiler)
: super(compiler)
{
}
//////////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////////
**
** Run the step
**
override Void run()
{
log.debug("ScanForUsingsAndTypes")
allTypes := Str:TypeDef[:]
units.each |CompilationUnit unit|
{
UsingAndTypeScanner(compiler, unit, allTypes).parse
}
bombIfErr
pod.typeDefs = allTypes
}
}
**************************************************************************
** UsingAndTypeScanner
**************************************************************************
class UsingAndTypeScanner : CompilerSupport
{
new make(Compiler compiler, CompilationUnit unit, Str:TypeDef allTypes)
: super(compiler)
{
this.unit = unit
this.tokens = unit.tokens
this.pos = 0
this.allTypes = allTypes
this.isSys = compiler.isSys
}
Void parse()
{
// sys is imported implicitly (unless this is sys itself)
if (!isSys)
unit.usings.add(Using(unit.loc) { podName="sys" })
// scan tokens quickly looking for keywords
inClassHeader := false
while (true)
{
tok := consume
if (tok.kind === Token.eof) break
switch (tok.kind)
{
case Token.usingKeyword:
parseUsing(tok)
case Token.lbrace:
inClassHeader = false
case Token.classKeyword:
case Token.mixinKeyword:
if (!inClassHeader)
{
inClassHeader = true;
parseType(tok);
}
}
}
}
private Void parseUsing(TokenVal tok)
{
u := Using(tok)
// using "some pod name"
if (curt === Token.strLiteral)
{
u.podName = consume(Token.strLiteral).val
}
else
{
// using [ffi]
u.podName = ""
if (curt === Token.lbracket)
{
consume
u.podName = "[" + consumeId + "]"
consume(Token.rbracket)
}
// using [ffi] pod
u.podName += consumeId
while (curt === Token.dot) // allow dots in pod name
{
consume
u.podName += "." + consumeId
}
}
// using [ffi] pod::type
if (curt === Token.doubleColon)
{
consume
u.typeName = consumeId
while (curt === Token.dollar) // allow $ in type name
{
consume
u.typeName += "\$"
if (curt == Token.identifier) u.typeName += consumeId
}
// using [ffi] pod::type as rename
if (curt === Token.asKeyword)
{
consume
u.asName = consumeId
}
}
unit.usings.add(u)
}
private Void parseType(TokenVal tok)
{
name := consumeId
typeDef := TypeDef(ns, tok, unit, name)
unit.types.add(typeDef)
// set mixin flag to use by Parser
if (tok.kind === Token.mixinKeyword)
typeDef.flags = typeDef.flags.or(FConst.Mixin)
// check for duplicate type names
if (allTypes.containsKey(name))
err("Duplicate type name '$name'", typeDef.loc)
else
allTypes[name] = typeDef
}
private Str consumeId()
{
id := consume
if (id.kind != Token.identifier)
{
err("Expected identifier", id)
return ""
}
return (Str)id.val
}
private Void verify(Token expected)
{
if (curt !== expected)
err("Expected '$expected.symbol', not '${tokens[pos]}'", tokens[pos]);
}
private TokenVal consume(Token? expected := null)
{
if (expected != null) verify(expected)
return tokens[pos++]
}
private Token curt()
{
return tokens[pos].kind
}
private CompilationUnit unit
private TokenVal[] tokens
private Int pos
private Bool isSys := false
private Str:TypeDef allTypes
}