Syntax_Analysis - nus-cs4215/x-slang-t1-xz-jj GitHub Wiki

Syntax Analysis/ Restriction

Each chapter of SICPy opens up new computing concepts for students to learn. Of course, this also means that there is a need to restrict some functionalities the students can access to in each chapter. SICPy accomplish this by analysing the Python AST that was generated from the students' source code.

More info about the AST can be found here. While the relevant implementation for this can be found under src/parser/parser_python.ts (we apologize for the bad naming).

The evaluate (aka analyzer) function that is responsible for analysing the AST can be found here.

function evaluate(component: JSON, context:object):any{
    return  is_Module(component)?Module_(component, context):
            is_Interactive(component)?Interactive_(component, context):
            is_Expression(component)?Expression_(component, context):
            is_Interactive(component)?Interactive_(component, context):
            is_Expression(component)?Expression_(component, context):
            is_FunctionType(component)?FunctionType_(component, context):
            is_FunctionDef(component)?FunctionDef_(component, context):
            is_AsyncFunctionDef(component)?AsyncFunctionDef_(component, context):
            ...

evaluate() takes in a component object, which is a AST expressed in JSON format. The context object consists of configurable values such as which SICPy chapter the user is currently using. The function then walk the AST sequentially, assessing if a functionality is allowed or disallowed.

The selector functions (e.g. is_Module, is_Interactive, etc) checks which of the Python syntaxes the AST is currently walking, and calls the corresponding analyzer function (e.g. Module_, Interactive(), etc). An example analyzer function for For is given below:

function For_(component, context){
    if(!check_rules("For", context['source_chapter']))
        return [{status:"no", msg:"For not allowed.. yet", "col_offset": component['col_offset'], "lineno": component['lineno']}]
    // console.log("In For_")
    var results=[]

    //target
    var target = component['target']
    if(target != null)
        results = results.concat(evaluate(target, context))
    //iter
    var iter = component['iter']
    if(iter != null)
        results = results.concat(evaluate(iter, context))

    //body
    var statements = component['body']
    for(idx in statements){
        var stmt = statements[idx]
        results = results.concat(evaluate(stmt, context))
    }

    //orelse
    var statements = component['orelse']
    for(idx in statements){
        var stmt = statements[idx]
        results = results.concat(evaluate(stmt, context))
    }
    return results
}

For have 4 sub-components namely target, iter, body and orelse. The analyser function will extract each sub-component and pass them into the evaluate function, so as to examine if any of the sub-components are using permitted syntaxes.

Switch board

SICPy allows an instructor to easily configure the allowable keywords/ syntaxes in each of the SICPy chapters through a logic switch board. The code for the switch board can be found here.

const rules = { '0' : 
                    {'Module':true,         'Interactive':false,  'Expression':false,
                    'FunctionType':false, 'FunctionDef':false,  'AsyncFunctionDef':false,
                    'ClassDef':false,       'Return':false,       'Delete':false,       'Assign':false,
                    'AugAssign':false,      'AnnAssign':false,    'For':false,          'AsyncFor':false,
                    'While':false,          'If':false,           'With':false,         'AsyncWith':false,
                    'Raise':false,          'Try':false,          'Assert':false,       'Import':false,
                    'ImportFrom':false,     'Global':false,       'Nonlocal':false,     'Expr':true,
                    'Pass':false,           'Break':false,        'Continue':false,     'BoolOp':true,
                    'NamedExpr':false,      'BinOp':true,        'UnaryOp':true,      'Lambda':false,
                    'IfExp':false,          'Dict':false,         'Set':false,          'ListComp':false,
                    'SetComp':false,        'DictComp':false,     'GeneratorExp':false, 'Await':false,
                    'Yield':false,          'YieldFrom':false,    'Compare':true,      'Call':false,
                    'FormattedValue':false, 'JoinedStr':false,    'Constant':true,     'Attribute':false,
                    'Subscript':false,      'Starred':false,      'Name':false,         'List':false,
                    'Tuple':false,          'Slice':false,        'attributes':false,   'Load':false,
                    'Store':false,          'Del':false,          'And':true,          'Or':true,
                    'Add':true,            'Sub':true,          'Mult':true,         'MatMult':false,
                    'Div':true,            'Mod':false,          'Pow':false,          'LShift':false,
                    'RShift':false,         'BitOr':false,        'BitXor':false,       'BitAnd':false,
                    'FloorDiv':false,       'Invert':false,       'Not':true,          'UAdd':true,
                    'USub':true,           'Eq':true,           'NotEq':true,        'Lt':true,
                    'LtE':true,             'Gt':true,           'GtE':true,          'Is':false,
                    'IsNot':false,          'In':false,           'NotIn':false},

An instructor can simply switch on or off a certain functionality above, by ascribing the value true or false such as shown in the table above.