// // 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 } }