///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // This project contains OMeta definitions for classes that are hosted by C++/.NET // and generate C++ code. These are saved in a workspace project to assist with // bootstrapping; they can be processed by the classes in OMeta_To_CPlusPlus to produce // the code for the second stage in a bootstrap process to get a version of OMeta // implemented in C++ that produces parsers in C++. Note that the host expressions // (rule arguments, semantic predicates, and semantic actions) in this file are in C++. // You won't be able to compile them directly in the workspace! // // Status: See project OMeta_To_CPlusPlus // // With some difficulty, you can arrange to compile the grammars here. // Sometimes, this gives you a useful indication of where a parse error // is. But not always. Probably need to replace the "do it" and/or "print it" // functions in the workspace to make this work right. // BEGIN: DO THIS ONLY ONCE! OrigOMetaParser = BSOMetaParser OrigOMetaTranslator = BSOMetaTranslator // END parseGrammar = function(g) { return OrigOMetaParser.matchAll(g, "grammar") } optimizeGrammar = function(g) { return BSOMetaOptimizer.match(parseGrammar(g),"optimizeGrammar") } compileGrammar = function (g) {return OrigOMetaTranslator.match(optimizeGrammar(g), "trans") } getGrammar = function () { var s = readFile("OMeta_To_CPlusPlus"); var g = arguments[0]; var re = new RegExp("\nometa " + g); var i = s.search(re); var s = s.substr(i); i = s.search(new RegExp("\n}")); return s.substr(0, i+3); } compileCrossGrammar = function() { eval(compileGrammar(getGrammar(arguments[0]))); } compileCrossGrammar("CPlusPlusishCodeJsToCPlusPlus") compileCrossGrammar("OMetaParserJsToCPlusPlus") compileCrossGrammar("OMetaTranslatorJsToCPlusPlus") BSOMetaParser = OMetaParserJsToCPlusPlus BSOMetaTranslator = OMetaTranslatorJsToCPlusPlus //-------------------------------------------------------------------------------- // // A minimal recognizer for delimited C++ expressions, suitable for picking // out the semantic actions, predicates, and rule arguments of C++ as a host // language. We only need to correctly count delimiters. // // TODO: The code generation step might benefit from converting blocks into lists // of statements. Consider the issues of open-coding And expressions. // ometa CPlusPlusishCode { // "Rules" to factor out some messy host expressions that operate on sequences. concat :xs -> {return xs;}, join :xs char:s -> {return xs + s;}, join :xs string:s -> {return xs + s;}, code = (parenExpr | block | sqChar | dqString | exactString | comment | chunk)+:xs concat{xs}, // Blocks are any valid constructs inside of braces. They might // be blocks of statements, or initialization blocks. block = '{' code*:xs '}' concat{xs}:x -> { return "{" + x + "}"; }, // Parenthesized expressions are any valid constructs inside of // parentheses. parenExpr = '(' code*:xs ')' concat{xs}:x -> { return "(" + x + ")"; }, // Chunks are arbitrary non-empty sequences of code, but never contain delimiters for // any quoted construct. chunk = (~('\"' | '{' | '}' | '(' | ')' | seq{"/*"} | seq{"//"} | seq{"@\""} | '\'') char)+:xs concat{xs}, eChar = '\\' char:c -> { return "\\" + c; } | char, sqChar = '\'' eChar:x '\'' -> { return "\'" + x + "\'"; }, dqString = '\"' (~'\"' eChar)*:xs '\"' concat{xs}:x -> { return "\"" + x + "\""; }, // The exact string is peculiar to C++, AFAIK. @"..." with no escapes recognized. exactString = '@' '\"' (~'\"' char)*:xs '\"' concat{xs}:x -> { return "@\"" + x + "\""; }, comment = fromTo{"//", "\n"} | fromTo{"/*", "*/"}, fromTo :x :y = seq{x}:a (~seq{y} char)*:bs seq{y}:c concat{bs}:b -> { return a + b + c; } } //-------------------------------------------------------------------------------- ometa Parser { token :x = spaces seq{x}, listOf :rule :s = apply{rule}:x (token{s} apply{rule})*:ys -> { var lst = new arraylist{ x }; lst += ys; return lst;} } //-------------------------------------------------------------------------------- ometa OMetaParser <: Parser { // "Rules" to factor out some messy host expressions that operate on sequences. concat :xs -> {return xs;}, join :xs char:s -> {return xs + s;}, join :xs string:s -> {return xs + s;}, fromTo :x :y = seq{x} (~seq{y} char)* seq{y}, space = super{"space"} | fromTo{"//", "\\n"} | fromTo{"/*", "*/"}, nameFirst = '_' | '$' | letter, nameRest = nameFirst | digit, tsName = firstAndRest{"nameFirst", "nameRest"}:xs concat{xs}, name = spaces tsName, eChar = '\\' char:c -> {return "\\" + (char)c;} | char, tsString = '\'' (~'\'' eChar)*:xs '\'' concat{xs}, characters = '`' '`' (~('\'' '\'') eChar)*:xs '\'' '\'' concat{xs}:x -> {return new arraylist {"App", "\"seq\"", "\"" + x + "\""};}, sCharacters = '"' (~'"' eChar)*:xs '"' concat{xs}:x -> {return new arraylist {"App", "\"token\"", "\"" + x + "\""};}, string = (('#' | '`') tsName | tsString):x -> {return new arraylist {"App", "\"exactly\"", "\"" + x + "\""};}, number = ('-' | empty -> {return "";}):sign digit+:ds concat{ds}:n -> {return new arraylist {"App", "\"exactly\"", sign + n};}, keyword :xs = token{xs} ~letterOrDigit -> {return xs;}, hostCode = foreign{new CPlusPlusishCode(), "code"}, args = "{" listOf{"hostCode", ','}:xs "}" -> {return xs;} | empty -> {return new arraylist();}, application = name:rule args:xs -> {var r = new arraylist {"App", "\""rule + "\""}; r += xs; return r;}, semAction = ("!" | "->") "{" hostCode:x "}" -> {return new arraylist {"Act", x};}, semPred = "?" "{" hostCode:x "}" -> {return new arraylist {"Pred", x};}, expr = listOf{"expr4", '|'}:xs -> {var r = new arraylist {"Or"}; r += xs; return r;}, expr4 = expr3*:xs -> {var r = new arraylist {"And"}; r += xs; return r;}, optIter :x = "*" -> {return new arraylist {"Many", x};} | "+" -> {return new arraylist {"Many1", x};} | empty -> {return x;}, expr3 = expr2:x optIter{x}:x ( ':' name:n -> { ((IList)this["locals"]).Add(n); return new arraylist {"Set", n, x}; } | empty -> {return x;} ) | ":" name:n -> { ((IList)this["locals"]).Add(n); return new arraylist {"Set", n, new arraylist {"App", "\"anything\""}}; }, expr2 = "~" expr2:x -> {return new arraylist {"Not", x};} | "&" expr1:x -> {return new arraylist {"Lookahead", x};} | expr1, expr1 = application | semAction | semPred | ( keyword{"undefined"} | keyword{"nil"} | keyword{"true"} | keyword{"false"} ):x -> {return new arraylist {"App", "\"exactly\"", x};} | spaces (characters | sCharacters | string | number) | "[" expr:x "]" -> {return new arraylist {"Form", x};} | "(" expr:x ")" -> {return x;}, ruleName = name | spaces tsString, rule = &(ruleName:n) !{this["locals"] = new ArrayList(); return true;} rulePart{n}:x ("," rulePart{n})*:xs -> {var r = new arraylist {"Or", x}; r += xs; return new arraylist {"Rule", n, this["locals"], r};}, rulePart :rn = ruleName:n ?{return Equals(n,rn);} expr4:b1 ( "=" expr:b2 -> {return new arraylist {"And", b1, b2 };} | empty -> {return b1;} ), grammar = keyword{"ometa"} name:n ( "<:" name | empty -> {return "OMeta";} ):sn "{" listOf{"rule", ','}:rs "}" -> {var r = new arraylist {"Grammar", n, sn}; r += rs; return r;} } //-------------------------------------------------------------------------------- ometa OMetaTranslator { // "Rules" to factor out some messy host expressions that operate on sequences. concat :xs -> {return xs;}, join :xs char:s -> {return xs + s;}, join :xs string:s -> {return xs + s;}, trans [:t apply{t}:ans] -> {return ans;}, transFn = trans:x asFnArg{x}, asFnArg = string:x -> {return "delegate {" + x + "}";}, App '\"super\"' :rule anything+:args join{args,", "}:a -> {return "return base.App(" + rule + ", " + a + ");";}, App '\"super\"' :rule -> {return "return base.App(" + rule + ");";}, App :rule anything+:args join{args,", "}:a -> {return "return App(" + rule + "," + a + ");";}, App :rule -> {return "return App(" + rule + ");";}, Act :expr -> {return expr;}, Pred :expr -> {return "return Pred( delegate\n{\n" + expr + "\n});";}, Or transFn*:xs join{xs,",\n"}:x -> {return "return Or(" + x + ");";}, And transFn*:xs join{xs,",\n"}:x -> {return "return And(" + x + ");";}, Many transFn:x -> {return "return Many(" + x + ");";}, Many1 transFn:x -> {return "return Many1(" + x + ");";}, Set :n transFn:v -> {return "return Set( ref " + n + ", " + v + ");";}, Not transFn:x -> {return "return Not(" + x + ");";}, Lookahead transFn:x -> {return "return Lookahead(" + x + ");";}, Form transFn:x -> {return "return Form(" + x + ");";}, Rule :name locals:ls trans:body -> {return new RuleDefinition(name, ls, body);}, // TODO: Figure out how to specify a namespace. Grammar :name :sName !{this["sName"] = sName; return true;} // MakeParserSource and the RuleDefinition type are provided by ParserBase. trans*:rules namespace:ns -> {return MakeParserSource((string)ns, name, sName, rules);}, locals = [] -> {return new arraylist();} | [apply{"unique"}], // Unique transforms the input stream into a list from which duplicates have been removed. unique = string:x ~any{x} unique:rest -> {var r = new arraylist {x}; r += rest; return r;} | string:x unique | empty -> {return new arraylist();}, // any{x} matches if the input stream contains any object that exactly matches x any :x = exactly{x} | anything any{x}, // This rule can be overridden to change the namespace: namespace = empty -> {return "OMetaCompiler";} }