diff --git a/syntactic-monoids/src/attributes.js b/syntactic-monoids/src/attributes.js
index 2dd5bfb736d5b3db71c84242e56133e118efa326..a31947a1df7edb738b45cd8545f7719ef7d25505 100644
--- a/syntactic-monoids/src/attributes.js
+++ b/syntactic-monoids/src/attributes.js
@@ -1,6 +1,10 @@
-// TODO: placeholder
+const READY = 0;
+const KEY = 1;
+const AFTER_EQUALS = 2;
+const VALUE = 3;
+const INVALID = 4;
 
-const STATE_COUNT = 0;
+const STATE_COUNT = 5;
 
 class MonoidElement {
   constructor(states) {
@@ -8,14 +12,52 @@ class MonoidElement {
   }
 
   get valid() {
-    return false; // TODO: stub
+    const finalState = this.states[READY];
+    return finalState === READY || finalState === VALUE;
   }
 }
 
-export const IDENTITY_ELEMENT = new MonoidElement([]); // TODO: placeholder
+export const IDENTITY_ELEMENT = new MonoidElement([
+  READY,
+  KEY,
+  AFTER_EQUALS,
+  VALUE,
+  INVALID,
+]);
+
+const SPACE = new MonoidElement([
+  READY,
+  INVALID,
+  INVALID,
+  READY,
+  INVALID,
+]);
+
+const EQUALS = new MonoidElement([
+  INVALID,
+  AFTER_EQUALS,
+  INVALID,
+  INVALID,
+  INVALID,
+]);
+
+const OTHER = new MonoidElement([
+  KEY,
+  KEY,
+  VALUE,
+  VALUE,
+  INVALID,
+]);
 
 export function encodeAsMonoidElement(character) {
-  return IDENTITY_ELEMENT; // TODO: stub
+  switch (character) {
+  case ' ':
+    return SPACE;
+  case '=':
+    return EQUALS;
+  default:
+    return OTHER;
+  }
 }
 
 export function combineMonoidElements(left, right) {
diff --git a/syntactic-monoids/src/quotes.js b/syntactic-monoids/src/quotes.js
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2c204b3054cd0b067bf69c4a06496b105ff67dd0 100644
--- a/syntactic-monoids/src/quotes.js
+++ b/syntactic-monoids/src/quotes.js
@@ -0,0 +1,58 @@
+const UNQUOTED = 0;
+const QUOTED = 1;
+const ESCAPED = 2;
+
+const STATE_COUNT = 3;
+
+class MonoidElement {
+  constructor(states) {
+    this.states = states;
+  }
+
+  get valid() {
+    return this.states[UNQUOTED] === UNQUOTED;
+  }
+}
+
+export const IDENTITY_ELEMENT = new MonoidElement([
+  UNQUOTED,
+  QUOTED,
+  ESCAPED,
+]);
+
+const QUOTE = new MonoidElement([
+  QUOTED,
+  UNQUOTED,
+  QUOTED,
+]);
+
+const BACKSLASH = new MonoidElement([
+  UNQUOTED,
+  ESCAPED,
+  QUOTED,
+]);
+
+const OTHER = new MonoidElement([
+  UNQUOTED,
+  QUOTED,
+  QUOTED,
+]);
+
+export function encodeAsMonoidElement(character) {
+  switch (character) {
+  case '"':
+    return QUOTE;
+  case '\\':
+    return BACKSLASH;
+  default:
+    return OTHER;
+  }
+}
+
+export function combineMonoidElements(left, right) {
+  const states = [];
+  for (let i = 0; i < STATE_COUNT; ++i) {
+    states.push(right.states[left.states[i]]);
+  }
+  return new MonoidElement(states);
+}