Skip to content

Commit 3ebdae6

Browse files
committed
final version
2 parents 9ab921f + 0c95af7 commit 3ebdae6

File tree

12 files changed

+596
-422
lines changed

12 files changed

+596
-422
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
.DS_Store
2+
full

grammar.js

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
const { rule, either, exactly, optional, minOf, token } = require('./rule-helpers');
2+
3+
// LineStatement -> IfExpressionStatement | AssignmentStatement | FunctionStatement
4+
const LineStatement = rule(
5+
() => either(IfExpressionStatement, AssignmentStatement, FunctionStatement),
6+
expression => expression
7+
);
8+
9+
// IfExpressionStatement -> IfKeyword PStart Expression PEnd CodeBlock
10+
const IfExpressionStatement = rule(
11+
() => exactly(IfKeyword, PStart, Expression, PEnd, CodeBlock),
12+
([,, check,, statements]) => ({ type: 'if', check, statements })
13+
)
14+
15+
// CodeBlock -> BStart LineStatement* BEnd
16+
const CodeBlock = rule(
17+
() => exactly(BStart, minOf(0, LineStatement), BEnd),
18+
([, statements]) => statements
19+
);
20+
21+
// FunctionStatement -> FunctionExpression Eol
22+
const FunctionStatement = rule(
23+
() => exactly(FunctionExpression, Eol),
24+
([expression]) => expression
25+
);
26+
27+
// FunctionExpression -> Name PStart FunctionParameters? PEnd
28+
const FunctionExpression = rule(
29+
() => exactly(Name, PStart, optional(FunctionParameters, []), PEnd),
30+
([name, _, parameters]) => ({
31+
type: 'function',
32+
name: name.value,
33+
parameters
34+
})
35+
);
36+
37+
// FunctionParameters -> Expression (Comma Expression)*
38+
const FunctionParameters = rule(
39+
() => exactly(Expression, minOf(0, exactly(Comma, Expression))),
40+
([first, rest]) => [first, ...rest.map(([_, parameter]) => parameter)]
41+
);
42+
43+
// AssignmentStatement -> Name Equals Expression Eol
44+
const AssignmentStatement = rule(
45+
() => exactly(Name, Equals, Expression, Eol),
46+
([name,, expression]) => ({ type: 'assignment', name: name.value, expression })
47+
);
48+
49+
// We use this functions for all binary operations in the
50+
// Expression rule because all of them parse the same way
51+
// this will allow us to create nested operations.
52+
const processBinaryResult = ([left, right]) => {
53+
let expression = left;
54+
55+
// We need to go through all operators on the right side
56+
// because there can be 3 or more operators in an expression.
57+
for (const [operator, rightSide] of right) {
58+
59+
// Each time we encounter an expression we put the
60+
// previous one in the left side.
61+
expression = {
62+
type: 'operation',
63+
operation: operator.value,
64+
left: expression,
65+
right: rightSide
66+
};
67+
}
68+
69+
// Finally we return the expression structure.
70+
return expression;
71+
};
72+
73+
// Expression -> EqualityTerm ((And | Or) EqualityTerm)*
74+
const Expression = rule(
75+
() => exactly(EqualityTerm, minOf(0, exactly(either(And, Or), EqualityTerm))),
76+
processBinaryResult
77+
);
78+
79+
// EqualityTerm -> RelationTerm ((DoubleEquals | NotEquals) RelationTerm)*
80+
const EqualityTerm = rule(
81+
() => exactly(RelationTerm, minOf(0, exactly(either(DoubleEquals, NotEquals), RelationTerm))),
82+
processBinaryResult
83+
);
84+
85+
// EqualityTerm -> AddSubTerm ((Less | Greater | LessEquals | GreaterEquals) AddSubTerm)*
86+
const RelationTerm = rule(
87+
() => exactly(AddSubTerm, minOf(0, exactly(either(Less, Greater, LessEquals, GreaterEquals), AddSubTerm))),
88+
processBinaryResult
89+
);
90+
91+
// AddSubTerm -> MulDivTerm ((Add | Subtract) MulDivTerm)*
92+
const AddSubTerm = rule(
93+
() => exactly(MulDivTerm, minOf(0, exactly(either(Add, Subtract), MulDivTerm))),
94+
processBinaryResult
95+
);
96+
97+
// MulDivTerm -> UnaryTerm ((Multiply | Divide) UnaryTerm)*
98+
const MulDivTerm = rule(
99+
() => exactly(UnaryTerm, minOf(0, exactly(either(Multiply, Divide), UnaryTerm))),
100+
processBinaryResult
101+
);
102+
103+
// UnaryTerm -> Not? Factor
104+
const UnaryTerm = rule(
105+
() => exactly(optional(Not), Factor),
106+
([addedNot, value]) => ({
107+
type: 'unary',
108+
withNot: addedNot.type !== 'optional',
109+
value
110+
})
111+
);
112+
113+
// Factor -> GroupExpression | FunctionExpression | NumberExpression | VariableExpression | StringExpression
114+
const Factor = rule(
115+
() => either(GroupExpression, FunctionExpression, NumberExpression, VariableExpression, StringExpression),
116+
factor => factor
117+
);
118+
119+
// GroupExpression -> PStart Expression PEnd
120+
const GroupExpression = rule(
121+
() => exactly(PStart, Expression, PEnd),
122+
([, expression]) => expression
123+
);
124+
125+
// VariableExpression -> Name
126+
const VariableExpression = rule(
127+
() => Name,
128+
name => ({
129+
type: 'variable',
130+
name: name.value
131+
})
132+
);
133+
134+
// NumberExpression -> Number
135+
const NumberExpression = rule(
136+
() => Number,
137+
number => ({
138+
type: 'number',
139+
value: number.value
140+
})
141+
);
142+
143+
// StringExpression -> String
144+
const StringExpression = rule(
145+
() => String,
146+
string => ({
147+
type: 'string',
148+
value: string.value
149+
})
150+
);
151+
152+
// Tokens
153+
const Number = token('number');
154+
const String = token('string');
155+
const Name = token('name');
156+
const Equals = token('operator', '=');
157+
const PStart = token('parenStart');
158+
const PEnd = token('parenEnd');
159+
const BStart = token('codeBlockStart');
160+
const BEnd = token('codeBlockEnd');
161+
const Comma = token('comma');
162+
const Eol = token('endOfLine');
163+
const IfKeyword = token('keyword', 'if');
164+
const And = token('operator', '&&');
165+
const Or = token('operator', '||');
166+
const DoubleEquals = token('operator', '==');
167+
const NotEquals = token('operator', '!=');
168+
const Less = token('operator', '<');
169+
const Greater = token('operator', '>');
170+
const LessEquals = token('operator', '<=');
171+
const GreaterEquals = token('operator', '>=');
172+
const Add = token('operator', '+');
173+
const Subtract = token('operator', '-');
174+
const Multiply = token('operator', '*');
175+
const Divide = token('operator', '/');
176+
const Not = token('operator', '!');
177+
178+
module.exports = LineStatement;

index.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,53 @@
1-
const run = require('./runner');
2-
1+
// Code which we want to parse
32
const code = `
43
firstName = 'John';
54
lastName = 'Smith';
65
age = 50;
76
8-
print('Entered name: ' + firstName + ', ' + lastName, 2);
7+
print('Entered name: ' + firstName + ', ' + lastName);
98
10-
if (age > 40 && checkName(firstName, 'John')) {
9+
if (age > 40) {
1110
if (age > 45) {
1211
print('Age is above 45');
1312
}
1413
print('Welcome, ' + firstName + ' you are ' + age + ' old.');
1514
}
1615
`;
1716

17+
// Import the lexer
18+
const analyseCode = require('./lexer-analyser');
19+
20+
// Run the lexer
21+
const tokens = analyseCode(code);
22+
23+
// Import the parser
24+
const parseTokens = require('./parser-analyser');
25+
26+
// Run the parser
27+
const statements = parseTokens(tokens);
28+
29+
30+
// Import the interpreter
31+
const interpret = require('./interpreter');
32+
33+
// We create a virtual state machine object
1834
const vm = {
1935
variables: {},
2036
functions: {
21-
print(text) {
22-
console.log('Printing:', text, ':)');
37+
print(message) { // We add a print function so that we can call a function from our code.
38+
console.log('MESSAGE:', message);
2339
},
24-
checkName(name, val) {
25-
return name === val;
40+
pow(x, y) { // We also add a function which returns something for an expression.
41+
return Math.pow(x, y);
2642
}
2743
}
2844
};
45+
// Interpret the statements and return last result.
46+
const result = interpret(statements, vm);
2947

30-
run(code, vm);
48+
// And finally we output the result
49+
console.log('Code we ran:');
50+
console.log(code);
51+
console.log('Result:')
52+
console.dir(result, {depth: null});
53+
console.log('Final VM State:', vm);

0 commit comments

Comments
 (0)