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 here
- expression: 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 nullif 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> tokenListandstring buffer, wheneverbufferis added totokenListremovebuffer
- Iterate over inputchar by char, do the following based on the char:- buffer, add it to- tokenList, otherwise continue
- (or- )if anything is in- buffer, add it, then add the current char either way
- &or- |or- ^then if there is something in- buffer, add that to- tokenList, 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 XorExprandParenExpr:
- 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:
 
 
- Idea 1: (leaning towards this one currently, allows for more expansion)
- 
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 inputincircuit.inputs- components.push([0,input])
 
- While components.length > 0- index, comp = components.shift
- indices[comp] = max(indices[comp], index)
- For componentin 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.