SC language grammar:
/* * Copyright (c) 2021. Jeffrey Vroom. All Rights Reserved. */ package sc.lang; import sc.lang.java.JavaModel; import sc.layer.Layer; import sc.obj.Constant; import sc.util.ExtensionFilenameFilter; import sc.parser.*; import sc.util.FileUtil; import java.io.File; import java.io.FilenameFilter; import java.io.StringReader; import java.util.*; /** * This class defines the grammar modifications to Java for the V language. Essentially: * - adds "object" as a class definition operator for defining instances * - support Component { for modifying an existing class. * - add a propertyAssignment option to the class body */ public class SCLanguage extends JavaLanguage { public static final String DEFAULT_EXTENSION = "sc"; static Set<IString> SC_KEYWORD_SET = new HashSet<IString>(Arrays.asList(PString.toPString(JAVA_KEYWORDS))); static Set<IString> SC_VARNAME_KEYWORD_SET = new HashSet<IString>(JAVA_VARNAME_KEYWORD_SET); static { SC_KEYWORD_SET.add(PString.toPString("object")); SC_KEYWORD_SET.add(PString.toPString("override")); SC_KEYWORD_SET.add(PString.toPString("scope")); SC_VARNAME_KEYWORD_SET.add(PString.toPString("object")); SC_VARNAME_KEYWORD_SET.add(PString.toPString("override")); SC_VARNAME_KEYWORD_SET.add(PString.toPString("scope")); } protected static final List<String> SC_CLASS_LEVEL_KEYWORDS = new ArrayList<String>(Arrays.asList("object", "override", "scope")); static { SC_CLASS_LEVEL_KEYWORDS.addAll(JavaLanguage.CLASS_LEVEL_KEYWORDS); } public Set getKeywords() { return SC_KEYWORD_SET; } public Set getVarNameKeywords() { return SC_VARNAME_KEYWORD_SET; } KeywordSpace scopeKeyword = new KeywordSpace("scope"); public Sequence scopeModifier = new Sequence("ScopeModifier(,,scopeName,)", scopeKeyword, lessThan, identifier, greaterThan); Sequence modifyDeclarationWithoutModifiers = new Sequence("ModifyDeclaration(typeName,typeParameters,extendsTypes,implementsTypes,,body,)", qualifiedIdentifier, optTypeParameters, extendsTypes, implementsTypes, openBraceEOL, classBodyDeclarations, closeBraceEOL); { // This prevents just "typeName" from parsing - we should need at least an open-brace after that. modifyDeclarationWithoutModifiers.minContentSlot = 4; } Sequence modifyDeclaration = new Sequence("(modifiers,.)", modifiers, modifyDeclarationWithoutModifiers); { assignmentOperator.add(":=:"); assignmentOperator.add("=:"); assignmentOperator.add(":="); variableInitializerOperators.add(":=:"); variableInitializerOperators.add("=:"); variableInitializerOperators.add(":="); modifiers.add(scopeModifier); modifiers.setName("<modifiers>([],[],[])"); classModifiers.add(scopeModifier); classModifiers.setName("<classModifiers>([],[],[])"); // In StrataCode, the package name is optional in the package definition, so you can override a package with no package in a subsequent layer in the layer definition file. packageDeclaration.set(annotations, new KeywordSpace("package"), optQualifiedIdentifier, semicolonEOL); } Sequence propertyAssignment = new Sequence("PropertyAssignment(propertyName,operator,initializer,)", identifierSp, assignmentOperator, variableInitializer, semicolonEOL); { propertyAssignment.minContentSlot = 1; } // This definition lets you override a field/property to attach annotations, change modifiers etc. Sequence overrideProperty = new Sequence("OverrideAssignment(, modifiers, propertyName,*,)", new SymbolSpace("override"), modifiers, identifierSp, new Sequence("(operator,initializer)", OPTIONAL, assignmentOperator, variableInitializer), semicolonEOL); // This definition lets you override a method's definition to attach annotations, and change modifiers on it without redefining the method. Sequence overrideMethod = new Sequence("MethodDefinition(override, modifiers,name,parameters,)", new KeywordSpace("override"), modifiers, identifierSp, formalParameters, semicolonEOL); Sequence mapEntry = new Sequence("MapEntry(key,,value)", expression, colon, expression); public OrderedChoice arrayVariableInitializer = new OrderedChoice("<arrayVariableInitializer>", variableInitializer, mapEntry); { // Redefine Java's type declaration parameter mapping and children to add the "modifyDeclaration". Just like Java use semicolon not semicolonEOL so we do not get skipOnError typeDeclaration.set("(.,.,.,)", classDeclaration, interfaceDeclaration, modifyDeclaration, semicolon); typeDeclarations.set("([],[],[],)", classDeclaration, interfaceDeclaration, modifyDeclaration, semicolon); // The operator is now a valid object. We are overriding the class declaration for now even though objects don't // support implements classOperators.add("object"); modifierKeywords.add("dynamic"); classModifierKeywords.add("dynamic"); // Add this to the index to the indexed choice for the new "object" keyword memberDeclaration.put("object", classDeclarationWithoutModifiers); // modify is a new default choice since there is no prefix to uniquely identify it - but make sure to match it before incompleteStatement memberDeclaration.addDefault(modifyDeclarationWithoutModifiers); classBodyDeclarations.setName("<classBodyDeclarations>([],[],,[],[],[],[])"); classBodyDeclarations.removeParselet(incompleteStatement); // Method should be ahead of property here for partial results to work properly since a property is a subset of a method when you take away the ; classBodyDeclarations.add(overrideMethod, overrideProperty, propertyAssignment, incompleteStatement); // Overrides the result class name used for this grammar. compilationUnit.setResultClassName("SCModel"); languageModel.setResultClassName("SCModel"); arrayInitializer.set( openBrace, new Sequence("([],[],)", OPTIONAL, arrayVariableInitializer, new Sequence("(,[])", REPEAT | OPTIONAL, comma, arrayVariableInitializer), new Sequence(OPTIONAL, comma)), closeBrace); } // These are used in CommandLineSCLanguage but needed here to allow us to convert this to JS Sequence expressionStatement = new Sequence("(.,)", expression, semicolonEOL); // These are optional in Java but for the command line we need to make them required Sequence reqPackageDeclaration = (Sequence) packageDeclaration.clone(); Sequence reqImport = (Sequence) imports.clone(); OrderedChoice reqClassBodyDeclaration = (OrderedChoice) classBodyDeclarations.clone(); Sequence optOpenBraceEOL = (Sequence) openBraceEOL.clone(); { reqClassBodyDeclaration.removeParselet(incompleteStatement); reqPackageDeclaration.optional = false; reqImport.setName("(.)"); reqImport.repeat = false; reqImport.optional = false; reqClassBodyDeclaration.setName("<reqClassBodyDeclarations>(.,.,,.,.,.)"); //reqClassBodyDeclaration.setName(reqClassBodyDeclaration.getName().replace("<classBodyDeclarations>", "<reqClassBodyDeclaration>")); reqClassBodyDeclaration.repeat = false; reqClassBodyDeclaration.skipOnErrorParselet = null; reqClassBodyDeclaration.optional = false; optOpenBraceEOL.optional = true; expressionStatement.setLanguage(this); // These are not used in this grammar by a top-level parselet so we need to init them here reqPackageDeclaration.setLanguage(this); reqImport.setLanguage(this); reqClassBodyDeclaration.setLanguage(this); optOpenBraceEOL.setLanguage(this); incompleteStatement.setLanguage(this); } @Constant public static SCLanguage INSTANCE = new SCLanguage(); public static SCLanguage getSCLanguage() { return INSTANCE; } public SCLanguage() { this(null); } public SCLanguage(Layer layer) { super(layer); addToSemanticValueClassPath("sc.lang.sc"); languageName = "StrataCode"; defaultExtension = DEFAULT_EXTENSION; } public static void main(String[] args) { SCLanguage c = (SCLanguage) Language.getLanguageByExtension(DEFAULT_EXTENSION); c.debug = false; parseFiles(c, inputFileNames, false); } public static final String STRATACODE_SUFFIX = "sc"; static final FilenameFilter STRATACODE_FILTER = new ExtensionFilenameFilter("." + STRATACODE_SUFFIX, true); static String [] inputFileNames = { "test1.sc" }; public static void parseFiles(SCLanguage c, Object[] inputFiles, boolean finalGenerate) { for (Object fileObj : inputFiles) { String fileName; File file; if (fileObj instanceof File) { file = (File) fileObj; fileName = file.toString(); } else { fileName = (String) fileObj; file = new File(fileName); } if (file.isDirectory()) { File[] files = file.listFiles(STRATACODE_FILTER); if (files == null) System.err.println("**** Unable to read directory: " + file); else parseFiles(c, files, finalGenerate); continue; } String input = ParseUtil.readFileString(file); if (input == null) { System.out.println("*** Can't open file: " + fileName); continue; } Object result = c.parse(fileName, new StringReader(input)); if (result == null || !input.equals(result.toString())) { if (result instanceof ParseError) System.out.println("File: " + fileName + ": " + ((ParseError) result).errorStringWithLineNumbers(file)); else { System.out.println("*** INPUTS DO NOT MATCH for file: " + fileName); System.out.println(input + " => " + result); } } else { IParseNode node = (IParseNode) result; System.out.println("inputs match for: " + fileName); System.out.println(); //if (result instanceof IParseNode) // System.out.println(((IParseNode)result).toDebugString()); //System.out.println("Model:" + node.getSemanticValue()); JavaModel m = (JavaModel) ParseUtil.getParseResult(node); System.out.println("Parsed model: " + m.toString()); /* ModelUtil.makeClassesBindable(m); System.out.println("External references: " + m.getExternalReferences()); */ //System.out.println("Bindable result: " + result); // Clear out the old parse-tree node.setSemanticValue(null, true, false); System.out.println(); // For debugging the generation rpocess only c.debug = true; // Generate a completely new one String genFileName = FileUtil.replaceExtension(fileName, "gen"); String genResult = c.generate(m, finalGenerate).toString(); FileUtil.saveStringAsFile(genFileName, genResult, true); System.out.println("*** processed: " + fileName); } } } public List<String> getClassLevelKeywords() { return SC_CLASS_LEVEL_KEYWORDS; } }