Frontends

C3 frontend

This is the c3 language front end.

For the front-end a recursive descent parser is created.

digraph c3 {
rankdir="LR"
1 [label="source text"]
10 [label="lexer" ]
20 [label="parser" ]
40 [label="code generation"]
99 [label="IR-code object"]
1 -> 10
10 -> 20
20 -> 40
40 -> 99
}
class ppci.lang.c3.AstPrinter

Prints an AST as text

class ppci.lang.c3.C3Builder(diag, arch_info)

Generates IR-code from c3 source.

Reports errors to the diagnostics system.

build(sources, imps=())

Create IR-code from sources.

Returns:A context where modules are living in and an ir-module.

Raises compiler error when something goes wrong.

do_parse(src, context)

Lexing and parsing stage (phase 1)

class ppci.lang.c3.CodeGenerator(diag)

Generates intermediate (IR) code from a package.

The entry function is ‘genModule’. The main task of this part is to rewrite complex control structures, such as while and for loops into simple conditional jump statements. Also complex conditional statements are simplified. Such as ‘and’ and ‘or’ statements are rewritten in conditional jumps. And structured datatypes are rewritten.

Type checking is done in one run with code generation.

emit(instruction, loc=None)

Emits the given instruction to the builder. Can be muted for constants.

error(msg, loc=None)

Emit error to diagnostic system and mark package as invalid

gen(context)

Generate code for a whole context

gen_assignment_stmt(code)

Generate code for assignment statement

gen_binop(expr: ppci.lang.c3.astnodes.Binop)

Generate code for binary operation

gen_bool_expr(expr)

Generate code for cases where a boolean value is assigned

gen_cond_code(expr, bbtrue, bbfalse)

Generate conditional logic. Implement sequential logical operators.

gen_dereference(expr: ppci.lang.c3.astnodes.Deref)

dereference pointer type, which means *(expr)

gen_expr_at(ptr, expr)

Generate code at a pointer in memory

gen_expr_code(expr: ppci.lang.c3.astnodes.Expression, rvalue=False) → ppci.ir.Value

Generate code for an expression. Return the generated ir-value

gen_external_function(function)

Generate external function

gen_for_stmt(code)

Generate for-loop code

gen_function(function)

Generate code for a function. This involves creating room for parameters on the stack, and generating code for the function body.

gen_function_call(expr)

Generate code for a function call

gen_global_ival(ival, typ)

Create memory image for initial value

gen_globals(module)

Generate global variables and modules

gen_identifier(expr)

Generate code for when an identifier was referenced

gen_if_stmt(code)

Generate code for if statement

gen_index_expr(expr)

Array indexing

gen_literal_expr(expr)

Generate code for literal

gen_local_var_init(var)

Initialize a local variable

gen_member_expr(expr)

Generate code for member expression such as struc.mem = 2 This could also be a module deref!

gen_module(mod: ppci.lang.c3.astnodes.Module)

Generate code for a single module

gen_return_stmt(code)

Generate code for return statement

gen_stmt(code: ppci.lang.generic.nodes.Statement)

Generate code for a statement

gen_switch_stmt(switch)

Generate code for a switch statement

gen_type_cast(expr)

Generate code for type casting

gen_unop(expr)

Generate code for unary operator

gen_while(code)

Generate code for while statement

get_debug_type(typ)

Get or create debug type info in the debug information

get_ir_function(function)

Get the proper IR function for the given function.

A new function will be created if required.

get_ir_type(cty)

Given a certain type, get the corresponding ir-type

is_module_ref(expr)

Determine whether a module is referenced

new_block()

Create a new basic block into the current function

class ppci.lang.c3.Context(arch_info)

A context is the space where all modules live in.

It is actually the container of modules and the top level scope.

equal_types(a, b, byname=False)

Compare types a and b for structural equavalence.

if byname is True stop on defined types.

eval_const(expr)

Evaluates a constant expression.

get_common_type(a, b, loc)

Determine the greatest common type.

This is used for coercing binary operators.

For example:

  • int + float -> float
  • byte + int -> int
  • byte + byte -> byte
  • pointer to x + int -> pointer to x
get_constant_value(const)

Get the constant value, calculate if required

get_module(name, create=True)

Gets or creates the module with the given name

get_type(typ, reveil_defined=True)

Get type given by str, identifier or type.

When reveil_defined is True, defined types are resolved to their backing types.

has_module(name)

Check if a module with the given name exists

is_simple_type(typ)

Determines if the given type is a simple type

Resolve all modules referenced by other modules

modules

Get all the modules in this context

pack_string(txt)

Pack a string an int as length followed by text data

resolve_symbol(ref)

Find out what is designated with x

size_of(typ)

Determine the byte size of a type

class ppci.lang.c3.Lexer(diag)

Generates a sequence of token from an input stream

tokenize(text)

Keeps track of the long comments

class ppci.lang.c3.Parser(diag)

Parses sourcecode into an abstract syntax tree (AST)

add_symbol(sym)

Add a symbol to the current scope

parse_cast_expression() → ppci.lang.c3.astnodes.Expression

Parse a cast expression.

The C-style type cast conflicts with ‘(‘ expr ‘)’ so introduce extra keyword ‘cast’.

parse_compound()

Parse a compound statement, which is bounded by ‘{‘ and ‘}’

parse_const_def()

Parse a constant definition

parse_const_expression()

Parse array initializers and other constant values

parse_designator()

A designator designates an object with a name.

parse_expression(rbp=0) → ppci.lang.c3.astnodes.Expression

Process expressions with precedence climbing.

See also:

http://eli.thegreenplace.net/2012/08/02/ parsing-expressions-by-precedence-climbing

parse_for() → ppci.lang.c3.astnodes.For

Parse a for statement

parse_function_def(public=True)

Parse function definition

parse_id_sequence()

Parse a sequence of id’s

parse_if()

Parse if statement

parse_import()

Parse import construct

parse_module(context)

Parse a module definition

parse_postfix_expression() → ppci.lang.c3.astnodes.Expression

Parse postfix expression

parse_primary_expression() → ppci.lang.c3.astnodes.Expression

Literal and parenthesis expression parsing

parse_return() → ppci.lang.c3.astnodes.Return

Parse a return statement

parse_source(tokens, context)

Parse a module from tokens

parse_statement() → ppci.lang.generic.nodes.Statement

Determine statement type based on the pending token

parse_switch() → ppci.lang.c3.astnodes.Switch

Parse switch statement

parse_top_level()

Parse toplevel declaration

parse_type_def(public=True)

Parse a type definition

parse_type_spec()

Parse type specification. Type specs are read from right to left.

A variable spec is given by: var [typeSpec] [modifiers] [pointer/array suffix] variable_name

For example: var int volatile * ptr; creates a pointer to a volatile integer.

parse_unary_expression()

Handle unary plus, minus and pointer magic

parse_variable_def(public=True)

Parse variable declaration, optionally with initialization.

parse_while() → ppci.lang.c3.astnodes.While

Parses a while statement

class ppci.lang.c3.Visitor(pre=None, post=None)

Visitor that can visit all nodes in the AST and run pre and post functions.

do(node)

Visit a single node

visit(node)

Visit a node and all its descendants

ppci.lang.c3.c3_to_ir(sources, includes, march, reporter=None)

Compile c3 sources to ir-code for the given architecture.

Brainfuck

The compiler has a front-end for the brainfuck language.

class ppci.lang.bf.BrainFuckGenerator(arch)

Brainfuck is a language that is so simple, the entire front-end can be implemented in one pass.

generate(src, module_name='main', function_name='main')

Takes a brainfuck program and returns the IR-code module

Fortran

This is the fortran frontend.

Currently this front-end is a work in progress.

class ppci.lang.fortran.FortranBuilder
class ppci.lang.fortran.parser.FortranParser

Parse some fortran language

class ppci.lang.fortran.FortranParser

Parse some fortran language

parse(src)

parse a piece of FORTRAN77

parse_assignment()

Parse an assignment

parse_declaration()

Parse variable declarations

parse_end()

Parse end statement

parse_expression(bp=0)

Welcome to expression parsing!

Solve this using precedence parsing with binding power.

parse_fmt_spec()

Parse a format specifier

parse_for()

Parse a for loop statement

parse_format()

Parse a format statement

parse_go()

Parse go to syntax

parse_label_ref()

Parse a label reference, this can be a number or.. a variable?

parse_program()

Parse a program

parse_unit_spec()

Parse a unit (file) specifier

class ppci.lang.fortran.Visitor

Visit ast nodes

class ppci.lang.fortran.Printer
print(node)

Print the AST

Llvm

Front-end for the LLVM IR-code

This front-end can be used as an enabler for many other languages, for example ADA and C++.

Currently this module a work in progress. The first step is to parse the llvm assembly. The next phase would be to convert that into ppci ir.

Another nice idea is to generate llvm ir code from ppci. When generating and parsing are combined, the llvm optimizers can be used.

class ppci.lang.llvmir.frontend.LlvmIrFrontend
class ppci.lang.llvmir.parser.LlvmIrParser(context)

Recursive descent parser for llvm-ir

Closely modeled after LLParser.cpp at: https://github.com/llvm-mirror/llvm/blob/master/lib/AsmParser/LLParser.cpp

ppci.lang.llvmir.llvm_to_ir(source)

Convert llvm assembly code into an IR-module

Example usage

An example usage is the following:

$ llvm-stress | ppci-llc.py -m riscv -o my_object.oj -

Here, the llvm-stress tool generates some random llvm source code, and this is piped into the ppci-llc.py command, which takes ll code and turns it into machine code.

Another example is how to use clang together with ppci-llc.py:

$ clang -S -emit-llvm -o - magic2.c | ppci-llc.py -o my_obj.oj -m msp430 -

This will compile the sourcefile magic2.c into my_obj.oj for the msp430 target.