Skip to content

Commit

Permalink
Start working on a Skeleton compiler. #1028
Browse files Browse the repository at this point in the history
  • Loading branch information
mayanje committed Aug 29, 2018
1 parent f747cc0 commit e598a9b
Show file tree
Hide file tree
Showing 14 changed files with 851 additions and 1 deletion.
39 changes: 39 additions & 0 deletions Codegen/src/IGeneratorEmitterCallback.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TypeCobol.Compiler.Nodes;
using TypeCobol.Compiler.Text;

namespace TypeCobol.Codegen
{
/// <summary>
/// Callback interface used by an IGenerator instance.
/// </summary>
public interface IGeneratorEmitterCallback
{
/// <summary>
/// Callback before starting generating the given node.
/// </summary>
/// <param name="node">The node</param>
/// <param name="generator">The generator</param>
/// <returns>true if the generation process can continue on this node, othewise no generation action
/// will be performed on this node, and this node and all its children will be marked as removed.</returns>
bool BeforeNode(Node node, IGenerator generator);
/// <summary>
/// Callback to get the an Enumeration of all lines to be generated for the given node.
/// </summary>
/// <param name="node">The node</param>
/// <param name="generator">The generator</param>
/// <returns>The IEnumerable instance</returns>
IEnumerable<ITextLine> Lines(Node Node, IGenerator generator);
/// <summary>
/// Callback after node generation.
/// </summary>
/// <param name="node">The node</param>
/// <param name="generator">The generator</param>
void AfterNode(Node node, IGenerator generator);

}
}
6 changes: 6 additions & 0 deletions TypeCobol.TemplateCompiler/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
31 changes: 31 additions & 0 deletions TypeCobol.TemplateCompiler/Config/ConfigParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.IO;
using System.Collections.Generic;
using TypeCobol.TemplateCompiler.Skeletons;

namespace TypeCobol.TemplateCompiler.Config {
/// <summary><see cref="Skeleton"/> parser interface.</summary>
public interface ConfigParser {
List<Skeleton> Parse(string path);
}

public class Config {
/// <summary>
/// The Map the association a configuration file extension to its Class parser.
/// </summary>
private static readonly Dictionary<string,Type> _parsers = new Dictionary<string,Type> {
{ ".xml", typeof(XmlParser) },
};

public static ConfigParser CreateParser(string extension) {
Type type;
try { type = _parsers[extension.ToLower()]; }
catch(System.Exception) { type = _parsers[".xml"]; }
return (ConfigParser)Activator.CreateInstance(type);
}

public static List<Skeleton> Parse(string path) {
return CreateParser(Path.GetExtension(path)).Parse(path);
}
}
}
211 changes: 211 additions & 0 deletions TypeCobol.TemplateCompiler/Config/XmlParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
using System.Xml;
using System.Collections.Generic;
using TypeCobol.TemplateCompiler.Skeletons;
using System.IO;

namespace TypeCobol.TemplateCompiler.Config {

/// <summary>XML Skeleton file parser<see cref="Skeleton"/> parser.</summary>
public class XmlParser: ConfigParser {
internal static string TAG_SKELETON = "skeleton";
internal static string TAG_CONDITION_LIST = "conditions";
internal static string TAG_CONDITION = "condition";
internal static string ATTR_NODE = "node";
internal static string ATTR_CLAUSE = "clause";
internal static string TAG_PATTERN_LIST = "patterns";
internal static string TAG_PATTERN = "pattern";
internal static string ATTR_NAME = "name";
internal static string ATTR_GROUP = "group";
internal static string ATTR_LOCATION = "location";
internal static string ATTR_ACTION = "action";
internal static string ATTR_VARIABLES = "var";
internal static string ATTR_POSITION = "position";
internal static string ATTR_NEWLINE = "newline";
internal static string ATTR_BOOLEAN_PROPERTY = "boolean_property";
internal static string ATTR_DEPRECATED = "deprecated";

/// <summary>Parses an XML file.</summary>
/// <param name="path">Path to an XML file</param>
/// <returns>List of <see cref="Skeleton"/> defined in <paramref name="path"/></returns>
public List<Skeleton> Parse(string path)
{
string[] files = null;
//If path contains "*"
if (path.Contains("*"))
{
string fileName = Path.GetFileName(path);
path = string.Concat(Path.GetDirectoryName(path), Path.DirectorySeparatorChar);
files = Directory.GetFiles(path, fileName);
}
//If path contains only directory.
else if (Path.GetFileName(path).Equals(string.Empty))
{
files = Directory.GetFiles(path, "*.*");
}
//If above conditions fail, it means the path has a specific file name.
else
{
files = new string[1];
files[0] = path;
}

var skeletons = new List<Skeleton>();
var factory = new SkeletonFactory();
foreach (var filePath in files)
{
var xml = new XmlDocument();
xml.Load(filePath);

foreach (var node in xml.ChildNodes)
{
var e = node as XmlElement;
if (e == null) continue;
foreach (var child in e.ChildNodes)
{
var ce = child as XmlElement;
if (ce == null) continue;
if (!ce.LocalName.ToLower().Equals(TAG_SKELETON)) continue;
skeletons.Add(factory.Create(ce));
}
}
}
return skeletons;
}

internal static string GetAttribute(XmlElement e, string name, string defaultvalue = null) {
string a = e.GetAttribute(name);
if (a == null || a.Length == 0) return defaultvalue;
return a;
}
internal static Dictionary<string,string> GetAttributes(XmlElement e) {
var attrs = new Dictionary<string,string>();
foreach(XmlAttribute attr in e.Attributes) {
attrs[attr.LocalName.ToLower()] = attr.Value;
}
return attrs;
}
}



interface Factory<T> {
/// <summary>Creates an object from a given <see cref="System.Xml.XMLElement"/>.</summary>
/// <typeparam name="T">Type of object created</param>
/// <param name="e">XML element</param>
/// <returns>Created object</returns>
T Create(XmlElement e);
}

internal class SkeletonFactory: Factory<Skeleton> {
private static ConditionListFactory CFactory = new ConditionListFactory();
private static PatternListFactory PFactory = new PatternListFactory();
public Skeleton Create(XmlElement e) {
var skeleton = new Skeleton();
skeleton.Conditions = new List<Condition>();
skeleton.Patterns = new List<Pattern>();
foreach(var child in e.ChildNodes) {
var ce = child as XmlElement;
if (ce == null) continue;
skeleton.Name = XmlParser.GetAttribute(e, XmlParser.ATTR_NAME);
string name = ce.LocalName.ToLower();
if (name.Equals(XmlParser.TAG_CONDITION_LIST)) {
skeleton.Conditions.AddRange(CFactory.Create(ce));
} else
if (name.Equals(XmlParser.TAG_PATTERN_LIST)) {
skeleton.Patterns.AddRange(PFactory.Create(ce));
}

}

skeleton.Properties = new List<string>();
string vars = e.GetAttribute(XmlParser.ATTR_VARIABLES);
foreach(var var in vars.Split(',')) {
string property = var.Trim();
if (property.Length > 0) skeleton.Properties.Add(property);
}

return skeleton;
}
}

internal class ConditionListFactory: Factory<List<Condition>> {
private static ConditionFactory Factory = new ConditionFactory();
public List<Condition> Create(XmlElement e) {
var conditions = new List<Condition>();
foreach(var child in e.ChildNodes) {
var ce = child as XmlElement;
if (ce == null) continue;
if (!ce.LocalName.ToLower().Equals(XmlParser.TAG_CONDITION)) continue;
conditions.Add(Factory.Create(ce));
}
return conditions;
}
}

internal class ConditionFactory: Factory<Condition> {
private Dictionary<string,List<string>> namespaces = new Dictionary<string,List<string>>();

public Condition Create(XmlElement e) {
var condition = new ConditionOnNode();
condition.Attributes = XmlParser.GetAttributes(e);
if (condition.Attributes.ContainsKey("node")) {
condition.Node = condition.Attributes["node"];
condition.Attributes.Remove("node");
}
return condition;
}
}

internal class PatternListFactory: Factory<List<Pattern>> {
private static PatternFactory Factory = new PatternFactory();
public List<Pattern> Create(XmlElement e) {
var patterns = new List<Pattern>();
foreach(var child in e.ChildNodes) {
var ce = child as XmlElement;
if (ce == null) continue;
if (!ce.LocalName.ToLower().Equals(XmlParser.TAG_PATTERN)) continue;
patterns.Add(Factory.Create(ce));
}
return patterns;
}
}

internal class PatternFactory: Factory<Pattern> {
public Pattern Create(XmlElement e) {
var pattern = new Pattern();
pattern.Name = XmlParser.GetAttribute(e, XmlParser.ATTR_NAME);
pattern.Group = XmlParser.GetAttribute(e, XmlParser.ATTR_GROUP);
pattern.Location = XmlParser.GetAttribute(e, XmlParser.ATTR_LOCATION);
pattern.Action = XmlParser.GetAttribute(e, XmlParser.ATTR_ACTION);
string val = XmlParser.GetAttribute(e, XmlParser.ATTR_NEWLINE);
pattern.NewLine = val != null ? val.Equals("true") : false;
pattern.BooleanProperty = XmlParser.GetAttribute(e, XmlParser.ATTR_BOOLEAN_PROPERTY);
pattern.Deprecated = XmlParser.GetAttribute(e, XmlParser.ATTR_DEPRECATED);
pattern.Variables = new Dictionary<string,string>();
string vars = e.GetAttribute(XmlParser.ATTR_VARIABLES);
foreach(var var in vars.Split(',')) {
var kv = var.Split('=');
if (kv.Length != 2) continue;
string key = kv[0].Trim();
string value = kv[1].Trim().ToLower();
pattern.Variables.Add(key, value);
}

string pos = XmlParser.GetAttribute(e, XmlParser.ATTR_POSITION);
if (pos != null)
{
try
{
pattern.Position = int.Parse(pos);
}
catch (System.FormatException)
{
//do not set pattern.Position
}
}

pattern.Template = e.InnerText;
return pattern;
}
}
}
99 changes: 99 additions & 0 deletions TypeCobol.TemplateCompiler/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using Mono.Options;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace TypeCobol.TemplateCompiler
{
class Program
{
/// <summary>
/// Program name from Assembly name
/// </summary>
public static string ProgName => Assembly.GetExecutingAssembly().GetName().Name;

/// <summary>
/// Assembly version
/// </summary>
public static string Version => Assembly.GetExecutingAssembly().GetName().Version.ToString();

/// <summary>
/// The Skeleton file in input.
/// </summary>
public static string SkeletonFile
{
get; set;
}

/// <summary>
/// The Output file.
/// </summary>
public static string OutputFile
{
get; set;
}

/// <summary>
/// Command Line Option Set
/// </summary>
public static OptionSet Options
{
get;
internal set;
}

static int Main(string[] args)
{
bool help = false;
bool version = false;

var p = new OptionSet()
{
"USAGE",
" "+ProgName+" [OPTIONS]",
"",
"VERSION:",
" "+Version,
"",
"DESCRIPTION:",
" Run the TypeCobol Template Compiler.",
{ "v|version","Show version", _ => version = true },
{ "h|help","Show help", _ => help = true },
{ "i|input=","{PATH} Template file path", (string v) => SkeletonFile = v },
{ "o|output=","{PATH} Generated output file path", (string v) => OutputFile = v }
};

System.Collections.Generic.List<string> arguments;
try { arguments = p.Parse(args); }
catch (OptionException ex) { return Exit(1, ex.Message); }

if (help)
{
p.WriteOptionDescriptions(System.Console.Out);
return 0;
}
if (version)
{
System.Console.WriteLine(Version);
return 0;
}
//Store the options.
Options = p;


return 0;
}

static int Exit(int code, string message)
{
string errmsg = ProgName + ": " + message + "\n";
errmsg += "Try " + ProgName + " --help for usage information.";
System.Console.WriteLine(errmsg);
return code;
}

}
}
Loading

0 comments on commit e598a9b

Please sign in to comment.