Language workbenches support the efficient creation, integration, and use of domain-specific languages. Typically, they execute models by code generation to programming language code. This can lead to increased productivity and higher quality. However, in safety-/mission-critical environments, generated code may not be considered trustworthy, because of the lack of trust in the generation mechanisms. This makes it harder to justify the use of language workbenches in such an environment. In this paper, we demonstrate an approach to use such tools in critical environments. We argue that models created with domain-specific languages are easier to validate and that the additional risk resulting from the transformation to code can be mitigated by a suitably designed transformation and verification architecture. We validate the approach with an industrial case study from the healthcare domain. We also discuss the degree to which the approach is appropriate for critical software in space, automotive, and robotics systems.
Language workbenches are touted as a promising technology to engineer languages for use in a wide range of domains, from programming to science to business. However, not many real-world case studies exist that evaluate the suitability of language workbench technology for this task. This paper contains such a case study. In particular, we evaluate the development of mbeddr, a collection of integrated languages and language extensions built with the Jetbrains MPS language workbench. mbeddr consists of 81 languages, with their IDE support, 34 of them C extensions. The mbeddr languages use a wide variety of notations—textual, tabular, symbolic and graphical—and the C extensions are modular; new extensions can be added without changing the existing implementation of C. mbeddr's development has spanned 10 person-years so far, and the tool is used in practice and continues to be developed. This makes mbeddr a meaningful case study of non-trivial size and complexity. The evaluation is centered around five research questions: language modularity, notational freedom and projectional editing, mechanisms for managing complexity, performance and scalability issues and the consequences for the development process. We draw generally positive conclusions; language engineering with MPS is ready for real-world use. However, we also identify a number of areas for improvement in the state of the art in language engineering in general, and in MPS in particular.
The definition of a projectional editor does not just specify the notation of a language, but also how users interact with the notation. Because of that it is easy to end up with different interaction styles within one and between multiple languages. The resulting inconsistencies have proven to be a major usability problem. To address this problem, we introduce grammar cells, an approach for declaratively specifying textual notations and their interactions for projectional editors. In the paper we motivate the problem, give a formal definition of grammar cells, and define their mapping to low-level editor behaviors. Our evaluation based on project experience shows that grammar cells improve editing experience by providing a consistent and intuitive ``text editor-like'' user experience for textual notations. At the same time they do not limit language composability and the use of non-textual notations, the primary benefits of projectional editors. We have implemented grammar cells for Jetbrains MPS, but they can also be used with other projectional editors.
We report on an industrial case study on developing the embedded software for a smart meter using the C programming language and domain-specific extensions of C such as components, physical units, state machines, registers and interrupts. We find that the extensions help significantly with managing the complexity of the software. They improve testability mainly by supporting hardware-independent testing, as illustrated by low integration efforts. The extensions also do not incur significant overhead regarding memory consumption and performance. Our case study relies on mbeddr, an extensible version of C. mbeddr, in turn, builds on the MPS language workbench which supports modular extension of languages and IDEs.
Language extension enables integration of new language constructs without invasive changes to a base language (e. g., C). Such extensions help to build more reliable software by using proper domain-specific abstractions. Language workbenches significantly reduce the effort for building such extensible languages by synthesizing a fully-fledged IDE from language definitions. However, in contemporary tools, this synthesis does not include interactive debugging for programs written with the base language or its extensions. This paper describes a generic framework for extensible debuggers that enables debugging of the language extensions by defining mappings between the base language and the language extensions. The architecture is designed for extensibility, so debug support for future extensions can be contributed with little effort. We show an implementation of our approach for mbeddr, which is an extensible version of the C programming language. We also discuss the debugger implementation for non-trivial C extensions such as components. Finally, the paper discusses the extent to which the approach can be used with other base languages, debugger backends and language workbenches.
Two innovations are enhancing programming languages' capabilities. First, modularity lets you combine independently developed languages without changing their respective definitions. A language is no longer a fixed quantity; you can extend it with domain-specific constructs as needed. Second, projectional editing lets you build editors and IDEs that don't require parsers. Such editors and IDEs support a range of tightly integrated notations, including textual, symbolic, tabular, and graphical notations. In addition, by avoiding parsers, they avoid grammar composition's well-known limitations. Three examples illustrate how these two innovations affect programming-language design. A set of modular extensions of C for embedded programming enables efficient code generation and formal analysis. A language for requirements engineering flexibly combines structured and unstructured (prose) data. Finally, a language for defining insurance rules uses mathematical notation. These examples all rely on the open source JetBrains MPS (Meta Programming System) language workbench. This article is part of a special issue on Software Architecture.
This paper explores the use of domain-specific languages for improving software security, which deals with developing software in a way that is not maliciously exploitable. Specifically we demonstrate how modular extension of the C programming language can help with technical and process-related aspects of software security. Some of these examples are already implemented, some are analytical extrapolations from related work we have done in the past; a detailed empirical evaluation has not yet been done. We rely on mbeddr, an extensible version of C developed with the JetBrains MPS language workbench. We conclude the paper with a discussion of the potential drawbacks of the approach and how these can be addressed in the future.
Today’s challenges for language development include language extension and composition, as well as the use of diverse notations. A promising approach is projectional editing, a technique to directly manipulate the abstract syntax tree of a program, without relying on parsers. Its potential lies in the ability to combine diverse notational styles – such as text, symbols, tables, and graphics – and the support for a wide range of composition techniques. However, projectional editing is often perceived as problematic for developers. Expressed drawbacks include the unfamiliar editing experience and challenges in the integration with existing infrastructure. In this paper we investigate the usability of projectional editors. We systematically identify usability issues resulting from the architecture. We use JetBrains Meta Programming System (MPS) as a case study. The case study discusses the concepts that MPS incorporates to address the identified issues, evaluates effectiveness of these concepts by surveying professional developers, and reports industrial experiences from realizing large-scale systems. Our results show that the benefits of flexible language composition and diverse notations come at the cost of serious usability issues – which, however, can be effectively mitigated with facilities that emulate editing experience of parser-based editors.
[Context/ Motivation] A smart device is a software-intensive system that operates autonomously and interacts to some degree with other systems over wireless connections. Such systems are often faced with uncertainty in the environment. Runtime representations of requirements have recently gained more interested to deal with this challenge and the term requirements at runtime has been established. Runtime representations of requirements support reasoning about the requirements at runtime and adapting the configuration of a system according to changes in the environment. [Questions/Problems] The research question is how the results of runtime monitoring of requirements and the system’s decisions about changes in the configuration are communicated back to the requirements engineer to better understand the environment. There is a gap between the written requirements document and the dynamic requirements model inside the system. This problem is exacerbated by the fact that a requirements document are mostly informal while the dynamic requirements model is formal. [Principal ideas/results] This paper introduces an approach to bridge the gap between development time and runtime representations of requirements in order to keep them consistent and to facilitate better understanding. We propose to weave the feedback from the runtime system into requirements documents using a domain-specific language that largely retain the informal nature of requirements. An annotated requirements document helps get a better understanding of the system’s actual behavior in a given environment. The approach is implemented using mbeddr, a novel set of domain-specific languages for developing embedded systems, and illustrated using a running example.
In this industrial presentation we will demonstrate mbeddr, an extensible set of integrated languages for embedded software development. After discussing the context of the talk, we will give details about the mbeddr architecture, which relies on the MPS language workbench. Then we will elaborate on the extension modules and show how they fit with safety-critical development processes. Finally we will point out how the existing languages can be extended by the user by giving some real-world examples, including a language construct that could have prevented the Apple 'goto fail' bug as well as mathematical notations.
The interactions of several languages within a software system pose a number of problems. There is several anecdotal and empirical evidence supporting such concerns. This paper presents a solution to achieve proper language integration in the context of language workbenches and with limited effort. A simple example is presented to show how cross-language constraints can be addressed and the quality of the support attainable, which covers error-checking and refactoring. A research agenda is then presented, to support future work in the area of language integration, taking advantage of modern language workbenches features.
Tools can boost software developer productivity, but building custom tools is prohibitively expensive, especially for small organizations. For example, embedded programmers often have to use low-level C with limited IDE support, and integrated into an off-the-shelf tool chain in an ad-hoc way.To address these challenges, we have built mbeddr, an extensible language and IDE for embedded software development based on C. mbeddr is a large-scale instantiation of the Jetbrains MPS language workbench. Exploiting its capabilities for language modularization and composition, projectional editing and multi-stage transformation, mbeddr is an open and modular framework that lets third parties add extensions to C with minimal effort and without invasive changes. End users can combine extensions in programs as needed.To illustrate the approach, in this paper we discuss mbeddr's support for state machines, components, decision tables, requirements tracing, product line variability and program verification and outline their implementation. We also present our experience with building mbeddr, which shows that relying on language workbenches dramatically reduces the effort of building customized, modular and extensible languages and IDEs to the point where this is affordable by small organizations. Finally, we report on the experience of using mbeddr in a commercial project, which illustrates the benefits to end users.
Developers who use C model checkers have to overcome three usability challenges: First, it is difficult to express application level properties as C-level verification conditions, due to the abstraction gap. Second, without advanced IDE support, it is difficult to interpret the counterexamples produced by the model checker and understand what went wrong in terms of application level properties. Third, most C model checkers support only a subset of C and it is easy for developers to inadvertently use C constructs outside this subset. In this paper we report on our preliminary experience with using the MPS language workbench to integrate the CBMC model checker with a set of domain-specific extensions of C for developing embedded software. Higher level language constructs such as components and decision tables makes it easier for end users to bridge the abstraction gap, to write verification conditions and to interpret the analysis results. Furthermore, the use of language workbenches allows the definition of analyzable language subsets, making the implementation of analyses simpler and their use more predictable.
This case study looks at the first large-scale demo case built with mbeddr: a set of C extensions for programming Lego Mindstorms robots. We have developed several robots (and the respective software) based on a common set of C extensions. Since C-based Mindstorms programming is based on the OSEK operating system, this case study has relevance beyond Lego. Last Changed: Feb 1, 2012 1 Contacts for more Info The Lego Mindstorms case study has been developed by the mbeddr team, in particular Bernd Kolb (kolb@itemis.de) and Markus Voelter (voelter@acm.org). 2 What is the problem/domain your system addresses? Lego Mindstorms provides the ability to program lego models. The system comes with an ARM processor and a number of sensors and actuators to build various kinds of computer-controlled machines. The software can be written in various ways, from a visual programming language provided by Lego, a version of Java, as well as C. In this case study we use the C-based way of programming mindstorms. The C-based way of programming Mindstorms is based on the OSEK operating system . OSEK is used a lot in automotive embedded software, which is why this case study is relevant beyond Lego. The OSEK implementation used for Mindstorms in Lejos OSEK . 3 Why was mbeddr/MPS chosen as the basis? mbeddr was used as the basis because this case study was explicitly intended to be an mbeddr demo. 4 How does the system integrate/use mbeddr’s existing capabilities? For example, a interface DriveTrain supports a high-level API for driving the robots. We use preand postconditions as well as a protocol state machine to define the semantics of the interface. exported c/s interface DriveTrain { void driveForwardFor(uint8 speed, uint32 ms) pre(0) speed <= 100 post(1) currentSpeed() == 0 protocol init(0) -> init(0) void driveBackwardFor(uint8 speed, uint32 ms) pre(0) speed <= 100 post(1) currentSpeed() == 0 protocol init(0) -> init(0) void driveContinouslyForward(uint8 speed) pre(0) speed <= 100 post(1) currentSpeed() == speed protocol init(0) -> new forward(1) void driveContinouslyBackward(uint8 speed) pre(0) speed <= 100 post(1) currentSpeed() == speed protocol init(0) -> new backward(2) query uint8 currentSpeed() void stop() post(0) currentSpeed() == 0 protocol *(-1) -> init(0) void turnLeft(uint8 turnDeltaSpeed) protocol init(0) -> init(0) void turnRight(uint8 turnDeltaSpeed) protocol init(0) -> init(0) } Components then implement the interfaces and keep global state. For example, the DriveTrainImpl component provides the DriveTrain interface and keeps track of the current speed, something that cannot easily be queried from the robot itself. The motors are encapsulated into interfaces/components as well. This way, we can provide dummy/mock implementations to simulate the robot without using the Mindstorms hardware and API. Other convenience components such as the Orienter use the drive train underneath and provide a high-level approach to orienting the robot based on the compass sensor. The compass sensor itself requires a non-trivial sequence of operations to retrieve an actual heading. This is encapsulated in the component. void orienter_orientTowards(int16 heading, uint8 speed, DIRECTION dir) <op orienter.orientTowards { int16 currentDir = compass.heading(); if ( dir == COUNTERCLOCKWISE ) { motorLeft.set_speed(-1 * ((int8) speed)); motorRight.set_speed(((int8) speed)); while ( !(currentDir in [heading 4 .. heading + 4]) ) { currentDir = compass.heading(); } } else { motorLeft.set_speed(((int8) speed)); motorRight.set_speed(-1 * ((int8) speed)); while ( !(currentDir in [heading 4 .. heading + 4]) ) { currentDir = compass.heading(); } } motorLeft.stop(); motorRight.stop(); } State Machines: One of the robots is configured to drive around an obstacle course. It has an ultrasound sensor that can detect obstacles and a a set of bumper sensors at the front and at the back. Whenever the robot detects an obstacle with one of these sensors, it backtracks, turns, and tries again. It uses slightly different strategies depending on which sensors report a (possible) collision. The logic that decides where to go when is implemented as a state machine. The state machine calls out to the above mentioned components to effect the necessary changes in direction or speed. Requirements Tracing: For demo purposes we have defined a set of requirements for the robot. They are captured with the mbeddr requirements language: We then use tracing from various implementation artifacts to connect these to requirements: Product Line Variability: Lego robots are particularly useful for building hardware product lines — you can just plug on/off various parts of the robot. In our case, for example, the bumpers and the compass were easily removable. The software of the robot has to take into account this variablity. Also, there is an optional display that is available, so display output must be removed if it is not configured. An example is below. 5 How was mbeddr extended? The extensions developed in this system related to first-class support for OSEK abstractions. These include: OIL Files: The OSEK operating system has to be configured for each application. This configuration determines various aspects of the OS instance including tasking, scheduling, and memory allocation. To be able to define these OIL files, we have implemented the OIL language in MPS. This is rather trivial, since OIL files are essentially nested name-value pairs. Note however, that OIL files themselves are not C extensions, they are a separate, stand-alone (”external”) DSL. An example is below. However, our language implementation does know about the various possible entries and their properties. The code completion menu below shows the various options available in OIL files. After selecting a task, the properties required by a task are pre-initialized; Events referenced by the task can be referenced directly. C Extensions for Tasks and Events A major reason why the OIL file declares tasks and events is that the operating system instance generated from an OIL file then schedules taks and manages events for the programmer. However, it is of course necessary to provide implementations of tasks (i.e. specify what should happen as the task executes). We have implemented a new top level content task that serves this purpose: task (SirenTask) { if ( siren.isOn() ) { siren.playOnce(); } TerminateTask(); } SirenTask in the code above is actually a reference to the task node declared in the OIL file of the particular system. This way it is ensured that you can only define task implementations for tasks declared in the OIL file. Conversely, the system reports a warning in the OIL file if there is no task implementation referring to a particular task declaration in the OIL file. The OSEK API provides various functions for managing events; the code below uses a few of them. task (Shoot) { while ( true ) { WaitEvent(ShootEvt); ClearEvent(ShootEvt); if ( ... ) { SetEvent(PoliceCarDriver, SignalHit); } } TerminateTask(); } In OSEK, the arguments passed into these API functions are simply integers. In mbeddr, we have built an extension, EventMaskType, which directly acts as a reference to the events declared in the OIL file; pressing Ctrl-Space directly shows the available events (ShootEvent and SignalHit are examples in the code above). This has obvious advantages for program consistency. The code below shows the declaration of these functions. external module kernel resources header : "kernel.h" header : <osek.h> { void TerminateTask(); void ActivateTask(TaskType task_type); void ChainTask(TaskType task_type); void ShutdownOS(StatusType status); StatusType SignalCounter(CounterType counter); StatusType WaitEvent(EventMaskType event); StatusType GetEvent(TaskType task, EventMaskType* event); StatusType ClearEvent(EventMaskType event); StatusType SetEvent(TaskType task, EventMaskType event); } Note how this external module ”wraps” the header files that define the API provided by OSEK. We redefine the functions using our own TaskType and EventMaskType. The reason why this works is that these types, when generated to C, are reduced to the same int types used by the original API. This way, while we can provide better IDE support and error checking in the IDE, the generated code is still compatible with the original API, without any overhead. Custom Build Support Lejos-OSEK comes with its own particular flavor of make files for building executables. Also, there are some peculiarities about how binaries are configured. For this reason we have built a new kind of platform that plugs into the mbeddr build system: The platform specifies the OIL file to be used for a given system and specifies that path to the build infrastructure provided by Lejos-OSEK. Also, a new generator for BuildConfigurations is provided which translates BuildConfigurations to valid Lejos-OSEK make files. 6 What is your conclusion? The main purpose of this system is to act as a demo for mbeddr. It serves this purpose well. Developing it helped us uncover and fix all kinds of bugs in the mbeddr system. However, the development also proved that the idea of mbeddr works: the components and state machines do make the code more readable, more flexible and easier to test. This is achieved without any significant runtime overhead — we were able to run the binaries on the Lego target hardware. It also showed that projector platform-specific C extensions are useful and can be built with very limited effort and without changing the C base language itself (the tasks, event types, etc.). OIL files demonstrate the integration of platform-specific artifacts into the overall system, integrating with type checking, error reporting and IDE support. Finally, the case study shows that custom build infrastructures (make file formats, compilers) can be integrated into mbeddr using the hooks provided for just that purpose. 7 Status? Is it in use yet? We have built seve
There is a big semantic gap between today's general purpose programming languages on the one hand and the input languages of formal verification tools on the other hand. This makes integrating formal analyses into the daily development practice artificially complex. In this paper we advocate that the use of language engineering techniques can substantially improve this situation along three dimensions. First, more abstract and thus more analyzable domain specific languages can be defined, avoiding the need for abstraction recovery from programs written in general purpose languages. Second, restrictions on the use of existing languages can be imposed and thereby more analyzable code can be obtained and analyses can be incrementally defined. Third, by expressing verification conditions and the verification results at the domain level, they are easier to define and the results of analyses are easier to interpret by end users. We exemplify our approach with three domain specific language fragments integrated into the C programming language, together with a set of analyses: completeness and consistency of decision tables, model-checking-based analyses for a dialect of state machines and consistency of feature models. The examples are based on the mbeddr stack, an extensible C language and IDE for embedded software development.
This case study looks at the first real-world mbeddr project that develops a smart meter. A smart meter is an electrical meter that continuously records the consumption of electrical power and sends the data back to the utility provider for monitoring and billing. The software comprises ca. 30.000 lines of mbeddr code. Several of the mbeddr default extensions are used, and additional, project-specific extensions have been developed. Three developers are involved in the project at this time, including people from the core mbeddr team as well as outsiders. While the project is still going on, we can already report some positive experiences and draw some conclusions. Last Changed: Sept 30, 2012 1 Contacts for more Info The SmartMeter system is developed by itemis France. Technical contacts are Bernd Kolb (kolb@itemis.de) and Stephan Eberle (eberle@itemis.de). 2 What is the problem/domain your system addresses? A smart meter is an electrical meter that continuously records the consumption of electric power in a home and sends the data back to the utility for monitoring and billing. The particular software we develop will run on a 2-chip board (TI MSP-430 for metrology, another TI-processor (tbd.) for the other application logic). Instead of the gcc compiler used in mbeddr by default, this project uses an IAR compiler. The software comprises ca. 30.000 lines of mbeddr code, has several timesensitive parts that require a low-overhead implementation and will have to be certified by the future operator. The software exploits existing code in the form of header files, libraries and code snippets. While the project is still going on, we can already report some experiences and draw some conclusions. 3 Why was mbeddr/MPS chosen as the basis? We chose mbeddr to implement the Smart Meter for the following reasons: – We have to work with an existing code base of questionable quality. We needed to improve the code quality incrementally. So starting with the existing C code and then refactoring towards better abstractions seemed like a good idea. – The existing C extensions provided by mbeddr are a good match for what is needed in Smart Meter (see next section). In particular, Smart Meter is not just a state-based or a data flow-based system, it contains multiple different behavioral paradigms. Using a state chart or data flow modeling tool was hence not an option. – As the goal is to have the meter certified, testing the software is very important. By using the abstraction mechanisms provided by mbeddr, and by exploiting the ability to build custom extensions, testability can be improved significantly. In particular, hardware-specifics can be isolated, which enables testing without the actual target hardware. Also, mbeddr’s support for requirements traceability comes in handy for the upcoming certification. 4 How does the system integrate/use mbeddr’s existing capabilities? In Smart Meter, we use the following default extensions: – Components: We use mbeddr’s components to improve the structure of the application, and to support different implementations of the same interface. This improves modularity and testability. Mocks components are used excessively for testing. – State Machines: The smart meter communicates with its environment via several different protocols. So far, one of these protocols has been refactored to use a state machine. This has proven to be much more readable than the original C code. We combined components and state machines which allowed us to decouple message assembly and parsing from the application logic in the server component. – Requirements Tracing: Smart Meter also make use of requirements traces. During the upcoming certification process, these will be extremely useful for tracking if and how the customer requirements have been implemented. Because of their orthogonal nature, the traces can be attached to the new language concepts specifically developed for Smart Meter (see next section). – Units: A major part of the Smart Meter application logic performs computations on physical quantities (time [s], current [A] or voltage [V]). So mbeddr’s support for physical units comes in handy. The benefits of this extensions are mostly in type checking, but some conversions are also generated into the resulting C code. Using types with units also improves the readability and comprehensibility of the code (important in Smart Meter, which relies on a significant existing code base). Summing up, the mbeddr default extensions have proven extremely useful in the development of Smart Meter. The fact that the extensions are directly integrated into C (as opposed to the classical approach of using external DSLs or separate modeling tools) reduces the hurdle of using higher-level extensions and removes any potential mismatch between DSL code and C code. It also support incrementally adding additional extensions as the need arises. 5 How was mbeddr extended? So far we made use of this possibility in the Smart Meter project in the following ways: – Registers: The Smart Meter software makes excessive use of registers (metrology: access the sensor values, UART: receive/send data). This cannot be abstracted away easily due to environmental constraints and how the software is written. In addition some registers are special purpose registers: when a value is written to such a register, a hardware-implemented computation is automatically triggered based on the value supplied by the programmer. The result of this computation is then stored in the register. If we want to run code that works with these registers on the PC for testing, we face two problems: first, the header files that define the addresses of the registers are not valid for the PC’s processor. Second, there are no special-purpose registers on the PC, so no automatic computations would be triggered. We solved this problem with a language extension that allows us to define registers first class and access them from C code (see code below). The extension also supports specifying an expression that performs the computation. When the code is translated for the real device, the real registers are accessed using the processor header files. In testing we use generated structs to hold the register data and insert the expression into the code that updates the struct, simulating the hardware-based computation. exported register8 ADC10CTL0 compute as val * 1000 void calculateAndStore( int8 value ) { int8 result = // some calculation with value ADC10CTL0 = result; // actually stores result * 1000 } – Interrupts: Many aspects of the Smart Meter system are driven by interrupts. To integrate the component-based architecture used in Smart Meter with interrupts, it is necessary to trigger component runnables (methods) via an interrupt. To this end, we have implemented a language extension that allows us to declare interrupts. In addition, the extension provides runnable triggers that express that a runnable is triggered by an interrupt. module Processor { exported interrupt USCI_A1 exported interrupt RTC } exported component RTCImpl { void interruptHandler() pRTCPS1CTL &= ~RT1PSIFG; } } The extension also provides a concept to assign an interrupt to a runnable during component instantiation. We also check that each interrupt-triggered runnable has at least one interrupt assigned. In addition, for testing purposes on the PC, we have language constructs that simulate the occurrence of an interrupt: the test driver simulates the triggering of interrupts based on a test-specified schedule and checks whether the system reacts correctly. – Emulation: Many parts of the Smart Meter application logic (beyond the component runnables discussed above) are triggered by interrupts, reacting to hardware events or timeouts. When testing the system on a PC, these external triggering events are not available, or course. So we started to write an emulation layer which will trigger interrupts based on various user-defined expressions. For example, an interrupt can be triggered every few seconds or once the value of a given register is changed. This will enable us to write integration tests. The emulator will not be a hardware emulator (emulating instructions) but rather a simple way of testing the interactions between different interrupt-triggered components. 6 What is your conclusion? mbeddr works for Smart Meter. The mbeddr default extensions have proven extremely useful in the development of Smart Meter. The fact that the extensions are directly integrated into C (as opposed to the classical approach of using external DSLs or separate modeling tools) reduces the hurdle of using higher-level extensions and removes any potential mismatch between DSL code and C code. Language Modularity Works. Building a language extension should not require changes to the base language. The extensions for Smart Meter demonstrate this point. The registers extension discussed above requires new top level module contents (the register definition themselves), new expressions (for reading and writing into the registers), and embedding expressions into new contexts (the code that simulates the hardware computation when registers are written). All of those have been built without changing C. Similarly, the interrupt-based runnable triggers have been hooked into the generic trigger facility that is part of the components language. Even the units extension, which provides new types, new literals, overloaded typing rules for operators and some adapted code generators has been developed in a modular way. Once a language is designed in a reasonable way, the language (or parts of it) should be reusable in contexts that had not been specifically anticipated in advance. The Smart Meter system contains examples: expressions have been embedded in the register definition for simulating the hardware behavior, and types with units have been used in decision tables. Again, no change to the existing languages has been necessary. La
While the C programming language provides good support for writing efficient, low-level code, it is not adequate for defining higher-level abstractions relevant to embedded software. In this paper we present the mbeddr technology stack that supports extension of C with constructs adequate for embedded systems. In mbeddr, efficient low-level programs can be written using the well-known concepts from C. Higher-level domain-specific abstractions can be seamlessly integrated into C by means of modular language extension regarding syntax, type system, semantics and IDE. In the paper we show how language extension can address the challenges of embedded software development and report on our experience in building these extensions. We show that language workbenches deliver on the promise of significantly reducing the effort of language engineering and the construction of corresponding IDEs. mbeddr is built on top of the JetBrains MPS language workbench. Both MPS and mbeddr are open source software.
M. Torchiano合作论文数Dept. of Control and Computer Engineering1