//
// Copyright (c) 2009, Brian Frank and Andy Frank
// Licensed under the Academic Free License version 3.0
//
// History:
// 15 May 09 Brian Frank Creation
//
**
** DslPlugin is the base class for Domain Specific Language plugins
** used to compile embedded DSLs. Subclasses are registered on
** the anchor type's qname with the "compiler.dsl.{anchor}" indexed
** prop and must declare a constructor with a Compiler arg.
**
abstract class DslPlugin : CompilerSupport
{
//////////////////////////////////////////////////////////////////////////
// Factory
//////////////////////////////////////////////////////////////////////////
**
** Find a DSL plugin for the given anchor type. If there
** is a problem then log an error and return null.
**
static DslPlugin? find(CompilerSupport c, Loc loc, CType anchorType)
{
// handle built-in ones to avoid index rebuild
qname := anchorType.qname
switch (qname)
{
case "sys::Str": return StrDslPlugin(c.compiler)
case "sys::Regex": return RegexDslPlugin(c.compiler)
}
// lookup via indexed props
t := Env.cur.index("compiler.dsl.${qname}")
if (t.size > 1)
{
c.err("Multiple DSL plugins registered for '$qname': $t", loc)
return null
}
if (t.size == 0)
{
c.err("No DSL plugin is registered for '$qname'", loc)
return null
}
try
{
return Type.find(t.first).make([c.compiler])
}
catch (Err e)
{
e.trace
c.errReport(CompilerErr("Cannot construct DSL plugin '$t.first'", loc, e))
return null
}
}
//////////////////////////////////////////////////////////////////////////
// Construction
//////////////////////////////////////////////////////////////////////////
**
** Constructor with associated compiler.
**
new make(Compiler c) : super(c) {}
//////////////////////////////////////////////////////////////////////////
// Namespace
//////////////////////////////////////////////////////////////////////////
**
** Compile DSL source into its Fantom equivalent expression.
** Log and throw compiler error if there is a problem.
**
abstract Expr compile(DslExpr dsl)
//////////////////////////////////////////////////////////////////////////
// Utils
//////////////////////////////////////////////////////////////////////////
**
** Normalize the DSL source using Fantom's multi-line whitespace
** rules where no non-whitespace chars may be appear to the left
** of the opening "<|" token. If source is formatted incorrectly
** then log and throw error.
**
Str normalizeSrc(DslExpr dsl)
{
// split the source lines, if single line just return it
lines := dsl.src.splitLines
if (lines.size == 1) return dsl.src
// walk each line and normalize it
s := StrBuf()
s.add(lines[0])
for (i:=1; i<lines.size; ++i)
{
s.addChar('\n')
line := lines[i]
// iterate thru each character until we've consumed
// matching number of leading tabs and spaces
numTabs := dsl.leadingTabs
numSpaces := dsl.leadingSpaces
j := 0
for (; j<line.size; ++j)
{
ch := line[j]
// consume leading tab or space
if (ch == '\t' || ch == ' ')
{
if (ch == '\t') --numTabs; else --numSpaces
if (numTabs == 0 && numSpaces == 0) break
continue
}
// if we made here, that means we have a non-whitespace
// char which is to the left of the opening "<|" token
loc := Loc(dsl.srcLoc.file, dsl.srcLoc.line+i, j+1)
if (dsl.leadingTabs == 0)
throw err("Leading space in $dsl.anchorType.name DSL must be $dsl.leadingSpaces spaces", loc)
else
throw err("Leading space in $dsl.anchorType.name DSL must be $dsl.leadingTabs tabs and $dsl.leadingSpaces spaces", loc)
}
if (j < line.size) s.add(line[j+1..-1])
}
return s.toStr
}
}