SC language grammar:
/* * Copyright (c) 2021. Jeffrey Vroom. All Rights Reserved. */ package sc.lang.js; import sc.lang.SCLanguage; import sc.layer.Layer; import sc.parser.*; import java.util.Set; /** * Language grammar for Javascript. This only supports the old-style javascript and right now requires semi-colons at * the end of each line. * * TODO: add a custom parselet for supporting optional semi-colons * TODO: add support for more modern syntax in JS * * Extending the SCLanguage to make it easier to convert from SC to JS via the grammar. Rather than defining a new * grammar from scratch, we replace and change the Java grammar so it can parse Javascript files. Partly this is * because we can then easily map parselets from one language to the other by looking up in the class hierarchy, but * we could have a more general way to do that (so for example that two sub-classes of Java, or two completely independent * languages map to each other based on mapping names or some import/export mechanism). The most important thing is that * during a conversion from one language to the next, you can just change the language of a parseNode and re-generate * it's code in the new language by applying the new grammar rules in reverse. That involves finding the parselet * in one language that maps to the next. * * The other way of looking at this is that some language needs to be the lingua-franca and the base language - it * doesn't really matter which one. Everything else gets derived from that one. It's not a bad model because it * ensures consistency in the name-space. */ public class JSLanguage extends SCLanguage implements IParserConstants { public final static JSLanguage INSTANCE = new JSLanguage(); // Switch back to using the Java keywords so we can use scope, object etc. as variable names // TODO: this should really be a separate set of keywords that takes into account JS specific reserved words public Set getKeywords() { return JAVA_KEYWORD_SET; } public Set getVarNameKeywords() { return JAVA_VARNAME_KEYWORD_SET; } public KeywordSpace varKeyword = new KeywordSpace("var"); public Sequence identifierList = new Sequence("([],[])", OPTIONAL, identifier, remainingIdentifiers); { // For JS, you can end statements with a ; either a newline or a line feed //endStatement.add("\n", "\r"); localVariableDeclaration.setName("VariableStatement(type,definitions)"); localVariableDeclaration.set(new Sequence("ClassType(typeName)", OPTIONAL, varKeyword), variableDeclarators); blockStatements.setName("([])"); blockStatements.set(statement); statement.removeParselet(syncStatement); statement.put("var", localVariableDeclarationStatement); // Be careful with the name here - there's one entry that's ommitted - the for a semicolon without anything in front of it. statement.setName("<statement>(.,.,.,.,.,.,.,.,.,.,.,.,,.,.,.)"); } public Sequence parameterNameList = new Sequence("(,.,)", openParen, identifierList, closeParen); public KeywordSpace functionKeyword = new KeywordSpace("function"); public Sequence functionDeclaration = new Sequence("JSFunctionDeclaration(,name,parameterNames,body)", functionKeyword, identifier, parameterNameList, block); public Sequence functionExpression = new Sequence("JSFunctionDeclaration(,name,parameterNames,body)", functionKeyword, optIdentifier, parameterNameList, block); public OrderedChoice sourceElement = new OrderedChoice("(.,.)", functionDeclaration, statement); Sequence sourceElements = new Sequence("([])", OPTIONAL | REPEAT, sourceElement); { languageModel = new Sequence("JSModel(,sourceElements)", spacing, sourceElements); setStartParselet(languageModel); primary.put("function", functionExpression); binaryOperators.add("==="); binaryOperators.add("!=="); // Javascript arrays use square brackets so to generate JS array expressions just replace the curly braces with sq brackets arrayInitializer.set(0, openSqBracket); arrayInitializer.set(2, closeSqBracket); // And JS allows you to just use arrays in normal expressions so we add the array to the set of primary expressions primary.put("[", arrayInitializer); // Javascript parameter declarations do not have types. For methods right now we explicitly format parameters so they are // never generated but for the 'catch' statement which uses the same grammar all we have to do to strip off the types is // to re-define the grammar for this type so that it is just a comma separated list of names. formalParameterDecls.setName("<formalParameterDecls>Parameter(*):(.)"); formalParameterDecls.set(formalParameterDeclRest); // Also need to redefine the catchParameter since it's has Type1 | TYPE2 which makes it different than formalParameters catchParameter.setName("CatchParameter(,*,)"); catchParameter.set(openParen, variableDeclaratorId, closeParenSkipOnError); // JS does not support the 'l' or "L" suffix integerLiteral.setName("IntegerLiteral(*)"); integerLiteral.remove(1); // No floating point type suffixes in javascript //floatTypeSuffix.setExpectedValues(new String[] {" "}); } public static JSLanguage getJSLanguage() { return INSTANCE; } public JSLanguage() { this(null); } public JSLanguage(Layer layer) { super(layer); // TODO: need to implement the rest of the JS grammar so we can parse a complete document. Right now, we're just // re-defining any lower level grammar objects for conversion from Java to JS. Ideally we could parse a JS and generate // a Java model from that as well, like to synchronize Java types from code we parse from a JS framework //setStartParselet(jsFile); addToSemanticValueClassPath("sc.lang.js"); } public String getOpenArray() { return "["; } public String getCloseArray() { return "]"; } public boolean getSupportsLongTypeSuffix() { return false; } }