Logical Expression to Circuit
Logical Expression to Circuit Design Doc
Trevor Crystal, Leon Montealegre (Last updated December 11, 2020)
Table of Contents
Overview
Creating large and complex circuits is time consuming. Most people who want to create and design circuits have a pretty good method of doing so:
- Create a truth table with how you want your circuit to function
- Convert the truth table into a logical expression
- Simplify that logical expression using methods such as Karnaugh Maps and Quine-McCluskey methods
- Manually turn your simplified logical expression into a circuit using a circuit designer (like OpenCircuits)
- Create the physical circuit
The goal for this issue is to tackle #4 of the workflow, i.e. allow users to enter a logical expression and automatically generate a circuit without having to build and design it manually.
Context
While we eventually plan on tackling every part of the workflow, the most time-consuming part is generally building a logical expression into a circuit. So to be able to take an expression and create a circuit from it would massively increase the productivity of the designer.
Goals & Non-Goals
Goals:
- Allows users to enter a logical expression and generate a circuit from it
- Also allow the ability to have the user choose to generate the circuit as an IC
Non-Goals:
- We do not plan on implementing truth table inputs for this issue
- We also do not plan on implementing a logical-simplification algorithm
Milestones
Existing Solution
Currently, it is possible to take any sort of logical expression and manually build an equivalent circuit. However, this is usually very time-consuming and can be extremely tedious.
Current User Walk-Through:
- A user decides on a logical expression they want to turn into a circuit
- They open the item nav and begin placing components
- They manually wire up all of the components
- They spend time testing the circuit to make sure it works
- They spend time debugging since they ended up making a few mistakes
- They spend time organizing the circuit so that it is visually appealing
- They again spend time testing the circuit to make sure it works
Proposed Solution
Proposed User Walk-Through:
- A user decides on a logical expression they want to turn into a circuit
- The user opens the circuit generation menu
- The user enter their logical expression into the field
- The user decides how they want their circuit generated (i.e. as an IC, as a circuit . with switches or buttons as inputs)
- The user confirms the generation and the circuit is pasted onto the circuit canvas
As can be seen by the walk-through, this is a much simpler and straightforward approach, that only involves a few button presses in contrast to the complicated process of manually building a circuit that is currently required.
Alternative Solutions
While we can always add many user-friendly enhancements to the circuit designer itself, there will always be user error in manually constructing a circuit, so this feature, while not required to make a fully-functioning circuit, is going to be incredibly helpful.
Testability
Many unit tests will need to be written for the main algorithm that takes in an expression and generates the circuit. All edge cases will need to be considered. Edge cases include proper precedence for the operators and correct evaluation of parenthesized operations. The core will simply output a circuit that should be easy to write unit tests for.
Impact
This feature should have no back-end requirements and will not contribute to potential user-information vulnerabilities.
The input of the logical expression is going to be the feature with the most potential for danger, it however, won't send any requests to the backend so no real sanitization will be necessary.
Large logical expressions could have some serious performance drawbacks. This needs to be considered and the algorithm designed efficiently. There might also be the necessity of some sort of loading indicator while the circuit is being generated. Along with this, we might also want to warn the user of large circuits and ask if they would rather have the circuit put into an IC (which has considerably better performance when placed).
Known Unknowns
- We are unsure exactly how we want the user to input the logical expression (although the mockup in Milestone 2 looks promising)
- Also unsure of where the menu for generating the circuit should be or how it should look in general (idea 1 for section 1 of Milestone 2 looks best, allows for expansibility to things like truth table)
- How will invalid inputs be handled? (at core algorithm level, null will be returned)
- Where on the canvas will the new circuit be placed, particularly if there is already another circuit? (may not really matter if the circuit is selectable once being placed)
Detailed Scoping
Milestone 1
- The main algorithm will be designed using a combination of my knowledge of LL parsing from the Programming Languages class and this article: https://www.meziantou.net/creating-a-parser-for-boolean-expressions.htm
- Operator precedence (following javascript precedence rules, maybe a later feature can allow custom precedence)
- ()
- !
- &
- ^ (xor)
- |
- This means & is evaluated before | so “a & b | c & d == (a & b) | (c & d)”
- Context-free grammar (boolean expressions are normally evaluated as left-associative, but since we are just building a circuit to represent the expression it should work as right associative)
Expr -> OrExpr
OrExpr -> XorExpr ‘|’ OrExpr | XorExpr
XorExpr -> AndExpr ‘^’ XorExpr | AndExpr
AndExpr -> NotExpr ‘&’ AndExpr | NotExpr
NotExpr -> ‘!’ ParenExpr | ParenExpr | ‘!’ NotExpr
ParenExpr -> ‘(’ Expr ‘)’ | input
- Core function prototype
- Parameters:
inputs
: links the names of inputs to a reference to the input that will be used, the final circuit will use the same DigitalComponent objects passed in hereexpression
: the expression to be parsed (caller should standardize the notation before calling, use & | ^ ! style operations)output
: type of output to send to (LED, etc.)- Will default to input port 0 of the item, the item will also be passed by reference for easy testing
- Returns:
- The circuit generated by the algorithm will be returned, the caller will have to change the positioning of the components to look nice
- Return
null
if there is any errors are encountered
function ExpressionToCircuit(inputs: Map<string,DigitalComponent>, expression: string, output: DigitalComponent): DigitalObjectSet
- Parameters:
- Pseudocode:
function GenerateTokens(input: string): Array<string>
- Create
Array<string> tokenList
andstring buffer
, wheneverbuffer
is added totokenList
removebuffer
- Iterate over
input
char by char, do the following based on the char:buffer
, add it totokenList
, otherwise continue(
or)
if anything is inbuffer
, add it, then add the current char either way&
or|
or^
then if there is something inbuffer
, add that totokenList
, then add the current char either way
- Return
tokenList
- Create
- Referring to cfg in section 3 of this milestone, each rule a-f will be a function, and they will function similar to the following examples for
XorExpr
andParenExpr
: function ParseXorExpr(tokens: Array<string>, int index, inputs: Map<string, DigitalComponent>): DigitalObjectSet, int
leftCircuit, index = ParseAndExpr(tokens, index, inputs)
if(index >= tokens.array || tokens[index] != ‘^’) return leftCircuit, index
index++
rightCircuit, newIndex = ParseXorExpr(tokens, index, inputs)
connected = leftCircuit and rightCircuit with Xor gate connecting
return connected, newIndex+1
function parenExpr(tokens: Array<string>, int index, inputs: Map<string, DigitalComponent>): DigitalObjectSet, int
if(tokens[index]==’(‘)
circuit, index = ParseExpr(tokens, index+1, inputs)
assert(tokens[index]==’)’)
return circuit, index+1
return inputs[tokens[index]], index+1
function ExpressionToCircuit(inputs: Map<string, string>, expression: string, output: DigitalComponent): DigitalObjectSet
tokens = GenerateTokens(string)
circuit = ParseExpr(tokens, 0, inputs)
return(add output to end of circuit)
- Use prototype to design tests, pseudocode to create algorithm
Milestone 2
Implement the menu button
- Idea 1: (leaning towards this one currently, allows for more expansion)
- Idea 2:
Implement the popup for the logical expression input
Rearrange the circuit so that it actually looks good on the canvas. Pseudocode:
placeCircuit(circuit: DigitalObjectSet)
- Create
Array<[int,component]> components
- Create
map<component, int> indices
- For
input
incircuit.inputs
components.push([0,input])
- While
components.length > 0
index, comp = components.shift
indices[comp] = max(indices[comp], index)
- For
component
in output ofcomp
components.push([index,comp])
- Iterate over the circuit, placing and connecting components so that each component is in column
indices[component]
- Create
Milestone 3
- Test the UI, perform tests on generating when there is already a circuit in place, etc.
- Lots of tests will need to be done to make sure the components of the circuit are placed in a visually comprehensible way (not all piled on top of eachother). This kind of testing can’t easily be automated.